1 /***************************************************************************
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011-2015 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 3 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/ **
24 ****************************************************************************/
26 #include "QCustomPlot.h"
30 ////////////////////////////////////////////////////////////////////////////////////////////////////
31 //////////////////// QCPPainter
32 ////////////////////////////////////////////////////////////////////////////////////////////////////
35 \brief QPainter subclass used internally
37 This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
38 consistency between antialiased and non-antialiased painting. Further it provides workarounds
41 \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
42 restore. So while it is possible to pass a QCPPainter instance to a function that expects a
43 QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
44 it will call the base class implementations of the functions actually hidden by QCPPainter).
48 Creates a new QCPPainter instance and sets default values
50 QCPPainter::QCPPainter() :
53 mIsAntialiasing(false)
55 // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
56 // a call to begin() will follow
60 Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
61 like the analogous QPainter constructor, begins painting on \a device immediately.
63 Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5.
65 QCPPainter::QCPPainter(QPaintDevice *device) :
68 mIsAntialiasing(false)
70 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
72 setRenderHint(QPainter::NonCosmeticDefaultPen);
76 QCPPainter::~QCPPainter()
81 Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
84 \note this function hides the non-virtual base class implementation.
86 void QCPPainter::setPen(const QPen &pen)
88 QPainter::setPen(pen);
89 if (mModes.testFlag(pmNonCosmetic))
95 Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
98 \note this function hides the non-virtual base class implementation.
100 void QCPPainter::setPen(const QColor &color)
102 QPainter::setPen(color);
103 if (mModes.testFlag(pmNonCosmetic))
109 Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
112 \note this function hides the non-virtual base class implementation.
114 void QCPPainter::setPen(Qt::PenStyle penStyle)
116 QPainter::setPen(penStyle);
117 if (mModes.testFlag(pmNonCosmetic))
123 Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
124 antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to
125 integer coordinates and then passes it to the original drawLine.
127 \note this function hides the non-virtual base class implementation.
129 void QCPPainter::drawLine(const QLineF &line)
131 if (mIsAntialiasing || mModes.testFlag(pmVectorized))
132 QPainter::drawLine(line);
134 QPainter::drawLine(line.toLine());
138 Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
139 with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
140 antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for
143 void QCPPainter::setAntialiasing(bool enabled)
145 setRenderHint(QPainter::Antialiasing, enabled);
146 if (mIsAntialiasing != enabled)
148 mIsAntialiasing = enabled;
149 if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
154 translate(-0.5, -0.5);
160 Sets the mode of the painter. This controls whether the painter shall adjust its
161 fixes/workarounds optimized for certain output devices.
163 void QCPPainter::setModes(QCPPainter::PainterModes modes)
169 Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a
170 device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5,
171 all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that
174 The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets
175 the render hint as appropriate.
177 \note this function hides the non-virtual base class implementation.
179 bool QCPPainter::begin(QPaintDevice *device)
181 bool result = QPainter::begin(device);
182 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
184 setRenderHint(QPainter::NonCosmeticDefaultPen);
191 Sets the mode of the painter. This controls whether the painter shall adjust its
192 fixes/workarounds optimized for certain output devices.
194 void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled)
196 if (!enabled && mModes.testFlag(mode))
198 else if (enabled && !mModes.testFlag(mode))
203 Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
204 QPainter, the save/restore functions are reimplemented to also save/restore those members.
206 \note this function hides the non-virtual base class implementation.
210 void QCPPainter::save()
212 mAntialiasingStack.push(mIsAntialiasing);
217 Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
218 QPainter, the save/restore functions are reimplemented to also save/restore those members.
220 \note this function hides the non-virtual base class implementation.
224 void QCPPainter::restore()
226 if (!mAntialiasingStack.isEmpty())
227 mIsAntialiasing = mAntialiasingStack.pop();
229 qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
234 Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen
235 overrides when the \ref pmNonCosmetic mode is set.
237 void QCPPainter::makeNonCosmetic()
239 if (qFuzzyIsNull(pen().widthF()))
248 ////////////////////////////////////////////////////////////////////////////////////////////////////
249 //////////////////// QCPScatterStyle
250 ////////////////////////////////////////////////////////////////////////////////////////////////////
252 /*! \class QCPScatterStyle
253 \brief Represents the visual appearance of scatter points
255 This class holds information about shape, color and size of scatter points. In plottables like
256 QCPGraph it is used to store how scatter points shall be drawn. For example, \ref
257 QCPGraph::setScatterStyle takes a QCPScatterStyle instance.
259 A scatter style consists of a shape (\ref setShape), a line color (\ref setPen) and possibly a
260 fill (\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can
261 be controlled with \ref setSize.
263 \section QCPScatterStyle-defining Specifying a scatter style
265 You can set all these configurations either by calling the respective functions on an instance:
266 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1
268 Or you can use one of the various constructors that take different parameter combinations, making
269 it easy to specify a scatter style in a single call, like so:
270 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2
272 \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable
274 There are two constructors which leave the pen undefined: \ref QCPScatterStyle() and \ref
275 QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \ref
276 isPenDefined will return false. It leads to scatter points that inherit the pen from the
277 plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line
278 color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes
279 it very convenient to set up typical scatter settings:
281 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation
283 Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works
284 because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly
285 into a QCPScatterStyle instance (that's the \ref QCPScatterStyle(ScatterShape shape, double size)
286 constructor with a default for \a size). In those cases, C++ allows directly supplying a \ref
287 ScatterShape, where actually a QCPScatterStyle is expected.
289 \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps
291 QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points.
293 For custom shapes, you can provide a QPainterPath with the desired shape to the \ref
294 setCustomPath function or call the constructor that takes a painter path. The scatter shape will
295 automatically be set to \ref ssCustom.
297 For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively you can use the
298 constructor that takes a QPixmap. The scatter shape will automatically be set to \ref ssPixmap.
299 Note that \ref setSize does not influence the appearance of the pixmap.
302 /* start documentation of inline functions */
304 /*! \fn bool QCPScatterStyle::isNone() const
306 Returns whether the scatter shape is \ref ssNone.
311 /*! \fn bool QCPScatterStyle::isPenDefined() const
313 Returns whether a pen has been defined for this scatter style.
315 The pen is undefined if a constructor is called that does not carry \a pen as parameter. Those are
316 \ref QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). If the pen is
317 left undefined, the scatter color will be inherited from the plottable that uses this scatter
323 /* end documentation of inline functions */
326 Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined.
328 Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
329 from the plottable that uses this scatter style.
331 QCPScatterStyle::QCPScatterStyle() :
341 Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or
344 Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
345 from the plottable that uses this scatter style.
347 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) :
357 Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
358 and size to \a size. No brush is defined, i.e. the scatter point will not be filled.
360 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
370 Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
371 the brush color to \a fill (with a solid pattern), and size to \a size.
373 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
377 mBrush(QBrush(fill)),
383 Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set to \a pen, the
384 brush to \a brush, and size to \a size.
386 \warning In some cases it might be tempting to directly use a pen style like <tt>Qt::NoPen</tt> as \a pen
387 and a color like <tt>Qt::blue</tt> as \a brush. Notice however, that the corresponding call\n
388 <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n
389 doesn't necessarily lead C++ to use this constructor in some cases, but might mistake
390 <tt>Qt::NoPen</tt> for a QColor and use the
391 \ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size)
392 constructor instead (which will lead to an unexpected look of the scatter points). To prevent
393 this, be more explicit with the parameter types. For example, use <tt>QBrush(Qt::blue)</tt>
394 instead of just <tt>Qt::blue</tt>, to clearly point out to the compiler that this constructor is
397 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
402 mPenDefined(pen.style() != Qt::NoPen)
407 Creates a new QCPScatterStyle instance which will show the specified \a pixmap. The scatter shape
408 is set to \ref ssPixmap.
410 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
421 Creates a new QCPScatterStyle instance with a custom shape that is defined via \a customPath. The
422 scatter shape is set to \ref ssCustom.
424 The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly
425 different meaning than for built-in scatter points: The custom path will be drawn scaled by a
426 factor of \a size/6.0. Since the default \a size is 6, the custom path will appear at a its
427 natural size by default. To double the size of the path for example, set \a size to 12.
429 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
434 mCustomPath(customPath),
435 mPenDefined(pen.style() != Qt::NoPen)
440 Sets the size (pixel diameter) of the drawn scatter points to \a size.
444 void QCPScatterStyle::setSize(double size)
450 Sets the shape to \a shape.
452 Note that the calls \ref setPixmap and \ref setCustomPath automatically set the shape to \ref
453 ssPixmap and \ref ssCustom, respectively.
457 void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)
463 Sets the pen that will be used to draw scatter points to \a pen.
465 If the pen was previously undefined (see \ref isPenDefined), the pen is considered defined after
466 a call to this function, even if \a pen is <tt>Qt::NoPen</tt>.
470 void QCPScatterStyle::setPen(const QPen &pen)
477 Sets the brush that will be used to fill scatter points to \a brush. Note that not all scatter
478 shapes have fillable areas. For example, \ref ssPlus does not while \ref ssCircle does.
482 void QCPScatterStyle::setBrush(const QBrush &brush)
488 Sets the pixmap that will be drawn as scatter point to \a pixmap.
490 Note that \ref setSize does not influence the appearance of the pixmap.
492 The scatter shape is automatically set to \ref ssPixmap.
494 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
501 Sets the custom shape that will be drawn as scatter point to \a customPath.
503 The scatter shape is automatically set to \ref ssCustom.
505 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
508 mCustomPath = customPath;
512 Applies the pen and the brush of this scatter style to \a painter. If this scatter style has an
513 undefined pen (\ref isPenDefined), sets the pen of \a painter to \a defaultPen instead.
515 This function is used by plottables (or any class that wants to draw scatters) just before a
516 number of scatters with this style shall be drawn with the \a painter.
520 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
522 painter->setPen(mPenDefined ? mPen : defaultPen);
523 painter->setBrush(mBrush);
527 Draws the scatter shape with \a painter at position \a pos.
529 This function does not modify the pen or the brush on the painter, as \ref applyTo is meant to be
530 called before scatter points are drawn with \ref drawShape.
534 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
536 drawShape(painter, pos.x(), pos.y());
540 Draws the scatter shape with \a painter at position \a x and \a y.
542 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
544 double w = mSize/2.0;
550 painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
555 painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
556 painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
561 painter->drawLine(QLineF(x-w, y, x+w, y));
562 painter->drawLine(QLineF( x, y+w, x, y-w));
567 painter->drawEllipse(QPointF(x , y), w, w);
572 QBrush b = painter->brush();
573 painter->setBrush(painter->pen().color());
574 painter->drawEllipse(QPointF(x , y), w, w);
575 painter->setBrush(b);
580 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
585 painter->drawLine(QLineF(x-w, y, x, y-w));
586 painter->drawLine(QLineF( x, y-w, x+w, y));
587 painter->drawLine(QLineF(x+w, y, x, y+w));
588 painter->drawLine(QLineF( x, y+w, x-w, y));
593 painter->drawLine(QLineF(x-w, y, x+w, y));
594 painter->drawLine(QLineF( x, y+w, x, y-w));
595 painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
596 painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
601 painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
602 painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
603 painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
606 case ssTriangleInverted:
608 painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
609 painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
610 painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
615 painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
616 painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
617 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
622 painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
623 painter->drawLine(QLineF( x, y+w, x, y-w));
624 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
629 painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
630 painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
631 painter->drawEllipse(QPointF(x, y), w, w);
636 painter->drawLine(QLineF(x-w, y, x+w, y));
637 painter->drawLine(QLineF( x, y+w, x, y-w));
638 painter->drawEllipse(QPointF(x, y), w, w);
643 painter->drawLine(QLineF(x, y-w, x, y+w));
644 painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
645 painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
646 painter->drawEllipse(QPointF(x, y), w, w);
651 painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
656 QTransform oldTransform = painter->transform();
657 painter->translate(x, y);
658 painter->scale(mSize/6.0, mSize/6.0);
659 painter->drawPath(mCustomPath);
660 painter->setTransform(oldTransform);
667 ////////////////////////////////////////////////////////////////////////////////////////////////////
668 //////////////////// QCPLayer
669 ////////////////////////////////////////////////////////////////////////////////////////////////////
672 \brief A layer that may contain objects, to control the rendering order
674 The Layering system of QCustomPlot is the mechanism to control the rendering order of the
675 elements inside the plot.
677 It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of
678 one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
679 QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers
680 bottom to top and successively draws the layerables of the layers.
682 A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base
683 class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
685 Initially, QCustomPlot has five layers: "background", "grid", "main", "axes" and "legend" (in
686 that order). The top two layers "axes" and "legend" contain the default axes and legend, so they
687 will be drawn on top. In the middle, there is the "main" layer. It is initially empty and set as
688 the current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
689 are created on this layer by default. Then comes the "grid" layer which contains the QCPGrid
690 instances (which belong tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background
691 shall be drawn behind everything else, thus the default QCPAxisRect instance is placed on the
692 "background" layer. Of course, the layer affiliation of the individual objects can be changed as
693 required (\ref QCPLayerable::setLayer).
695 Controlling the ordering of objects is easy: Create a new layer in the position you want it to
696 be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with
697 QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will
698 be placed on the new layer automatically, due to the current layer setting. Alternatively you
699 could have also ignored the current layer setting and just moved the objects with
700 QCPLayerable::setLayer to the desired layer after creating them.
702 It is also possible to move whole layers. For example, If you want the grid to be shown in front
703 of all plottables/items on the "main" layer, just move it above "main" with
704 QCustomPlot::moveLayer.
706 The rendering order within one layer is simply by order of creation or insertion. The item
707 created last (or added last to the layer), is drawn on top of all other objects on that layer.
709 When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
710 the deleted layer, see QCustomPlot::removeLayer.
713 /* start documentation of inline functions */
715 /*! \fn QList<QCPLayerable*> QCPLayer::children() const
717 Returns a list of all layerables on this layer. The order corresponds to the rendering order:
718 layerables with higher indices are drawn above layerables with lower indices.
721 /*! \fn int QCPLayer::index() const
723 Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
724 accessed via \ref QCustomPlot::layer.
726 Layers with higher indices will be drawn above layers with lower indices.
729 /* end documentation of inline functions */
732 Creates a new QCPLayer instance.
734 Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead.
736 \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot.
737 This check is only performed by \ref QCustomPlot::addLayer.
739 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
741 mParentPlot(parentPlot),
743 mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
746 // Note: no need to make sure layerName is unique, because layer
747 // management is done with QCustomPlot functions.
750 QCPLayer::~QCPLayer()
752 // If child layerables are still on this layer, detach them, so they don't try to reach back to this
753 // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
754 // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
755 // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
757 while (!mChildren.isEmpty())
758 mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
760 if (mParentPlot->currentLayer() == this)
761 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.";
765 Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
766 layer will be invisible.
768 This function doesn't change the visibility property of the layerables (\ref
769 QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
770 visibility of the parent layer into account.
772 void QCPLayer::setVisible(bool visible)
779 Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
780 be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
782 This function does not change the \a mLayer member of \a layerable to this layer. (Use
783 QCPLayerable::setLayer to change the layer of an object, not this function.)
787 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
789 if (!mChildren.contains(layerable))
792 mChildren.prepend(layerable);
794 mChildren.append(layerable);
796 qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
801 Removes the \a layerable from the list of this layer.
803 This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
804 to change the layer of an object, not this function.)
808 void QCPLayer::removeChild(QCPLayerable *layerable)
810 if (!mChildren.removeOne(layerable))
811 qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
815 ////////////////////////////////////////////////////////////////////////////////////////////////////
816 //////////////////// QCPLayerable
817 ////////////////////////////////////////////////////////////////////////////////////////////////////
819 /*! \class QCPLayerable
820 \brief Base class for all drawable objects
822 This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
825 Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
826 the layers accordingly.
828 For details about the layering mechanism, see the QCPLayer documentation.
831 /* start documentation of inline functions */
833 /*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
835 Returns the parent layerable of this layerable. The parent layerable is used to provide
836 visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables
837 only get drawn if their parent layerables are visible, too.
839 Note that a parent layerable is not necessarily also the QObject parent for memory management.
840 Further, a layerable doesn't always have a parent layerable, so this function may return 0.
842 A parent layerable is set implicitly with when placed inside layout elements and doesn't need to be
843 set manually by the user.
846 /* end documentation of inline functions */
847 /* start documentation of pure virtual functions */
849 /*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
852 This function applies the default antialiasing setting to the specified \a painter, using the
853 function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when
854 \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing
855 setting may be specified individually, this function should set the antialiasing state of the
856 most prominent entity. In this case however, the \ref draw function usually calls the specialized
857 versions of this function before drawing each entity, effectively overriding the setting of the
858 default antialiasing hint.
860 <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
861 line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased,
862 QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't
863 only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
864 antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
865 QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
866 calls the respective specialized applyAntialiasingHint function.
868 <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
869 setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
870 all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
871 respective layerable subclass.) Consequently it only has the normal
872 QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
873 care about setting any antialiasing states, because the default antialiasing hint is already set
874 on the painter when the \ref draw function is called, and that's the state it wants to draw the
878 /*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
881 This function draws the layerable with the specified \a painter. It is only called by
882 QCustomPlot, if the layerable is visible (\ref setVisible).
884 Before this function is called, the painter's antialiasing state is set via \ref
885 applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was
886 set to \ref clipRect.
889 /* end documentation of pure virtual functions */
890 /* start documentation of signals */
892 /*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
894 This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
900 /* end documentation of signals */
903 Creates a new QCPLayerable instance.
905 Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
908 If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a
909 targetLayer is an empty string, it places itself on the current layer of the plot (see \ref
910 QCustomPlot::setCurrentLayer).
912 It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later
913 time with \ref initializeParentPlot.
915 The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable
916 parents are mainly used to control visibility in a hierarchy of layerables. This means a
917 layerable is only drawn, if all its ancestor layerables are also visible. Note that \a
918 parentLayerable does not become the QObject-parent (for memory management) of this layerable, \a
919 plot does. It is not uncommon to set the QObject-parent to something else in the constructors of
920 QCPLayerable subclasses, to guarantee a working destruction hierarchy.
922 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
926 mParentLayerable(parentLayerable),
932 if (targetLayer.isEmpty())
933 setLayer(mParentPlot->currentLayer());
934 else if (!setLayer(targetLayer))
935 qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
939 QCPLayerable::~QCPLayerable()
943 mLayer->removeChild(this);
949 Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
950 on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not
953 void QCPLayerable::setVisible(bool on)
959 Sets the \a layer of this layerable object. The object will be placed on top of the other objects
962 If \a layer is 0, this layerable will not be on any layer and thus not appear in the plot (or
963 interact/receive events).
965 Returns true if the layer of this layerable was successfully changed to \a layer.
967 bool QCPLayerable::setLayer(QCPLayer *layer)
969 return moveToLayer(layer, false);
973 Sets the layer of this layerable object by name
975 Returns true on success, i.e. if \a layerName is a valid layer name.
977 bool QCPLayerable::setLayer(const QString &layerName)
981 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
984 if (QCPLayer *layer = mParentPlot->layer(layerName))
986 return setLayer(layer);
989 qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
995 Sets whether this object will be drawn antialiased or not.
997 Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
998 QCustomPlot::setNotAntialiasedElements.
1000 void QCPLayerable::setAntialiased(bool enabled)
1002 mAntialiased = enabled;
1006 Returns whether this layerable is visible, taking the visibility of the layerable parent and the
1007 visibility of the layer this layerable is on into account. This is the method that is consulted
1008 to decide whether a layerable shall be drawn or not.
1010 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
1011 subclasses, like in the case of QCPLayoutElement), this function returns true only if this
1012 layerable has its visibility set to true and the parent layerable's \ref realVisibility returns
1015 If this layerable doesn't have a direct layerable parent, returns the state of this layerable's
1018 bool QCPLayerable::realVisibility() const
1020 return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1024 This function is used to decide whether a click hits a layerable object or not.
1026 \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
1027 shortest pixel distance of this point to the object. If the object is either invisible or the
1028 distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the
1029 object is not selectable, -1.0 is returned, too.
1031 If the object is represented not by single lines but by an area like a \ref QCPItemText or the
1032 bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In
1033 these cases this function thus returns a constant value greater zero but still below the parent
1034 plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99).
1036 Providing a constant value for area objects allows selecting line objects even when they are
1037 obscured by such area objects, by clicking close to the lines (i.e. closer than
1038 0.99*selectionTolerance).
1040 The actual setting of the selection state is not done by this function. This is handled by the
1041 parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified
1042 via the selectEvent/deselectEvent methods.
1044 \a details is an optional output parameter. Every layerable subclass may place any information
1045 in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot
1046 decides on the basis of this selectTest call, that the object was successfully selected. The
1047 subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part
1048 objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked
1049 is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be
1050 placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
1051 selected doesn't have to be done a second time for a single selection operation.
1053 You may pass 0 as \a details to indicate that you are not interested in those selection details.
1055 \see selectEvent, deselectEvent, QCustomPlot::setInteractions
1057 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1060 Q_UNUSED(onlySelectable)
1067 Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
1068 passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to
1071 Note that, unlike when passing a non-null parent plot in the constructor, this function does not
1072 make \a parentPlot the QObject-parent of this layerable. If you want this, call
1073 QObject::setParent(\a parentPlot) in addition to this function.
1075 Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
1076 make the layerable appear on the QCustomPlot.
1078 The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized
1079 so they can react accordingly (e.g. also initialize the parent plot of child layerables, like
1082 void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)
1086 qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1091 qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1093 mParentPlot = parentPlot;
1094 parentPlotInitialized(mParentPlot);
1099 Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not
1100 become the QObject-parent (for memory management) of this layerable.
1102 The parent layerable has influence on the return value of the \ref realVisibility method. Only
1103 layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be
1108 void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)
1110 mParentLayerable = parentLayerable;
1115 Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
1116 the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
1117 false, the object will be appended.
1119 Returns true on success, i.e. if \a layer is a valid layer.
1121 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1123 if (layer && !mParentPlot)
1125 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1128 if (layer && layer->parentPlot() != mParentPlot)
1130 qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1134 QCPLayer *oldLayer = mLayer;
1136 mLayer->removeChild(this);
1139 mLayer->addChild(this, prepend);
1140 if (mLayer != oldLayer)
1141 emit layerChanged(mLayer);
1147 Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a
1148 localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref
1149 QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
1150 controlled via \a overrideElement.
1152 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1154 if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1155 painter->setAntialiasing(false);
1156 else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1157 painter->setAntialiasing(true);
1159 painter->setAntialiasing(localAntialiased);
1164 This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
1165 of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the
1166 parent plot is set at a later time.
1168 For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
1169 QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
1170 element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To
1171 propagate the parent plot to all the children of the hierarchy, the top level element then uses
1172 this function to pass the parent plot on to its child elements.
1174 The default implementation does nothing.
1176 \see initializeParentPlot
1178 void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)
1180 Q_UNUSED(parentPlot)
1185 Returns the selection category this layerable shall belong to. The selection category is used in
1186 conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and
1189 Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref
1190 QCP::iSelectOther. This is what the default implementation returns.
1192 \see QCustomPlot::setInteractions
1194 QCP::Interaction QCPLayerable::selectionCategory() const
1196 return QCP::iSelectOther;
1201 Returns the clipping rectangle of this layerable object. By default, this is the viewport of the
1202 parent QCustomPlot. Specific subclasses may reimplement this function to provide different
1205 The returned clipping rect is set on the painter before the draw function of the respective
1208 QRect QCPLayerable::clipRect() const
1211 return mParentPlot->viewport();
1218 This event is called when the layerable shall be selected, as a consequence of a click by the
1219 user. Subclasses should react to it by setting their selection state appropriately. The default
1220 implementation does nothing.
1222 \a event is the mouse event that caused the selection. \a additive indicates, whether the user
1223 was holding the multi-select-modifier while performing the selection (see \ref
1224 QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled
1225 (i.e. become selected when unselected and unselected when selected).
1227 Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e.
1228 returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).
1229 The \a details data you output from \ref selectTest is fed back via \a details here. You may
1230 use it to transport any kind of information from the selectTest to the possibly subsequent
1231 selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable
1232 that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need
1233 to do the calculation again to find out which part was actually clicked.
1235 \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must
1236 set the value either to true or false, depending on whether the selection state of this layerable
1237 was actually changed. For layerables that only are selectable as a whole and not in parts, this
1238 is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the
1239 selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the
1240 layerable was previously unselected and now is switched to the selected state.
1242 \see selectTest, deselectEvent
1244 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1249 Q_UNUSED(selectionStateChanged)
1254 This event is called when the layerable shall be deselected, either as consequence of a user
1255 interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by
1256 unsetting their selection appropriately.
1258 just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must
1259 return true or false when the selection state of this layerable has changed or not changed,
1262 \see selectTest, selectEvent
1264 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1266 Q_UNUSED(selectionStateChanged)
1270 ////////////////////////////////////////////////////////////////////////////////////////////////////
1271 //////////////////// QCPRange
1272 ////////////////////////////////////////////////////////////////////////////////////////////////////
1274 \brief Represents the range an axis is encompassing.
1276 contains a \a lower and \a upper double value and provides convenience input, output and
1277 modification functions.
1279 \see QCPAxis::setRange
1283 Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
1284 intervals would cause errors due to the 11-bit exponent of double precision numbers,
1285 corresponding to a minimum magnitude of roughly 1e-308.
1286 \see validRange, maxRange
1288 const double QCPRange::minRange = 1e-280;
1291 Maximum values (negative and positive) the range will accept in range-changing functions.
1292 Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
1293 corresponding to a maximum magnitude of roughly 1e308.
1294 Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
1296 \see validRange, minRange
1298 const double QCPRange::maxRange = 1e250;
1301 Constructs a range with \a lower and \a upper set to zero.
1303 QCPRange::QCPRange() :
1310 Constructs a range with the specified \a lower and \a upper values.
1312 QCPRange::QCPRange(double lower, double upper) :
1320 Returns the size of the range, i.e. \a upper-\a lower
1322 double QCPRange::size() const
1328 Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1330 double QCPRange::center() const
1332 return (upper+lower)*0.5;
1336 Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
1339 void QCPRange::normalize()
1342 qSwap(lower, upper);
1346 Expands this range such that \a otherRange is contained in the new range. It is assumed that both
1347 this range and \a otherRange are normalized (see \ref normalize).
1349 If \a otherRange is already inside the current range, this function does nothing.
1353 void QCPRange::expand(const QCPRange &otherRange)
1355 if (lower > otherRange.lower)
1356 lower = otherRange.lower;
1357 if (upper < otherRange.upper)
1358 upper = otherRange.upper;
1363 Returns an expanded range that contains this and \a otherRange. It is assumed that both this
1364 range and \a otherRange are normalized (see \ref normalize).
1368 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1370 QCPRange result = *this;
1371 result.expand(otherRange);
1376 Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
1377 the range won't span the positive and negative sign domain, i.e. contain zero. Further
1378 \a lower will always be numerically smaller (or equal) to \a upper.
1380 If the original range does span positive and negative sign domains or contains zero,
1381 the returned range will try to approximate the original range as good as possible.
1382 If the positive interval of the original range is wider than the negative interval, the
1383 returned range will only contain the positive interval, with lower bound set to \a rangeFac or
1384 \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
1385 is wider than the positive interval, this time by changing the \a upper bound.
1387 QCPRange QCPRange::sanitizedForLogScale() const
1389 double rangeFac = 1e-3;
1390 QCPRange sanitizedRange(lower, upper);
1391 sanitizedRange.normalize();
1392 // can't have range spanning negative and positive values in log plot, so change range to fix it
1393 //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1394 if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1397 if (rangeFac < sanitizedRange.upper*rangeFac)
1398 sanitizedRange.lower = rangeFac;
1400 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1401 } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1402 else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1405 if (-rangeFac > sanitizedRange.lower*rangeFac)
1406 sanitizedRange.upper = -rangeFac;
1408 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1409 } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1411 // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1412 if (-sanitizedRange.lower > sanitizedRange.upper)
1414 // negative is wider, do same as in case upper is 0
1415 if (-rangeFac > sanitizedRange.lower*rangeFac)
1416 sanitizedRange.upper = -rangeFac;
1418 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1421 // positive is wider, do same as in case lower is 0
1422 if (rangeFac < sanitizedRange.upper*rangeFac)
1423 sanitizedRange.lower = rangeFac;
1425 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1428 // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1429 return sanitizedRange;
1433 Returns a sanitized version of the range. Sanitized means for linear scales, that
1434 \a lower will always be numerically smaller (or equal) to \a upper.
1436 QCPRange QCPRange::sanitizedForLinScale() const
1438 QCPRange sanitizedRange(lower, upper);
1439 sanitizedRange.normalize();
1440 return sanitizedRange;
1444 Returns true when \a value lies within or exactly on the borders of the range.
1446 bool QCPRange::contains(double value) const
1448 return value >= lower && value <= upper;
1452 Checks, whether the specified range is within valid bounds, which are defined
1453 as QCPRange::maxRange and QCPRange::minRange.
1454 A valid range means:
1455 \li range bounds within -maxRange and maxRange
1456 \li range size above minRange
1457 \li range size below maxRange
1459 bool QCPRange::validRange(double lower, double upper)
1461 return (lower > -maxRange &&
1463 qAbs(lower-upper) > minRange &&
1464 qAbs(lower-upper) < maxRange &&
1465 !(lower > 0 && qIsInf(upper/lower)) &&
1466 !(upper < 0 && qIsInf(lower/upper)));
1471 Checks, whether the specified range is within valid bounds, which are defined
1472 as QCPRange::maxRange and QCPRange::minRange.
1473 A valid range means:
1474 \li range bounds within -maxRange and maxRange
1475 \li range size above minRange
1476 \li range size below maxRange
1478 bool QCPRange::validRange(const QCPRange &range)
1480 return (range.lower > -maxRange &&
1481 range.upper < maxRange &&
1482 qAbs(range.lower-range.upper) > minRange &&
1483 qAbs(range.lower-range.upper) < maxRange &&
1484 !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
1485 !(range.upper < 0 && qIsInf(range.lower/range.upper)));
1489 ////////////////////////////////////////////////////////////////////////////////////////////////////
1490 //////////////////// QCPMarginGroup
1491 ////////////////////////////////////////////////////////////////////////////////////////////////////
1493 /*! \class QCPMarginGroup
1494 \brief A margin group allows synchronization of margin sides if working with multiple layout elements.
1496 QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that
1497 they will all have the same size, based on the largest required margin in the group.
1500 \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
1503 In certain situations it is desirable that margins at specific sides are synchronized across
1504 layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will
1505 provide a cleaner look to the user if the left and right margins of the two axis rects are of the
1506 same size. The left axis of the top axis rect will then be at the same horizontal position as the
1507 left axis of the lower axis rect, making them appear aligned. The same applies for the right
1508 axes. This is what QCPMarginGroup makes possible.
1510 To add/remove a specific side of a layout element to/from a margin group, use the \ref
1511 QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call
1512 \ref clear, or just delete the margin group.
1514 \section QCPMarginGroup-example Example
1516 First create a margin group:
1517 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1
1518 Then set this group on the layout element sides:
1519 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2
1520 Here, we've used the first two axis rects of the plot and synchronized their left margins with
1521 each other and their right margins with each other.
1524 /* start documentation of inline functions */
1526 /*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const
1528 Returns a list of all layout elements that have their margin \a side associated with this margin
1532 /* end documentation of inline functions */
1535 Creates a new QCPMarginGroup instance in \a parentPlot.
1537 QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :
1538 QObject(parentPlot),
1539 mParentPlot(parentPlot)
1541 mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1542 mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1543 mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1544 mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1547 QCPMarginGroup::~QCPMarginGroup()
1553 Returns whether this margin group is empty. If this function returns true, no layout elements use
1554 this margin group to synchronize margin sides.
1556 bool QCPMarginGroup::isEmpty() const
1558 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1559 while (it.hasNext())
1562 if (!it.value().isEmpty())
1569 Clears this margin group. The synchronization of the margin sides that use this margin group is
1570 lifted and they will use their individual margin sizes again.
1572 void QCPMarginGroup::clear()
1574 // make all children remove themselves from this margin group:
1575 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1576 while (it.hasNext())
1579 const QList<QCPLayoutElement*> elements = it.value();
1580 for (int i=elements.size()-1; i>=0; --i)
1581 elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1587 Returns the synchronized common margin for \a side. This is the margin value that will be used by
1588 the layout element on the respective side, if it is part of this margin group.
1590 The common margin is calculated by requesting the automatic margin (\ref
1591 QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin
1592 group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into
1595 int QCPMarginGroup::commonMargin(QCP::MarginSide side) const
1597 // query all automatic margins of the layout elements in this margin group side and find maximum:
1599 const QList<QCPLayoutElement*> elements = mChildren.value(side);
1600 for (int i=0; i<elements.size(); ++i)
1602 if (!elements.at(i)->autoMargins().testFlag(side))
1604 int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1613 Adds \a element to the internal list of child elements, for the margin \a side.
1615 This function does not modify the margin group property of \a element.
1617 void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)
1619 if (!mChildren[side].contains(element))
1620 mChildren[side].append(element);
1622 qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1627 Removes \a element from the internal list of child elements, for the margin \a side.
1629 This function does not modify the margin group property of \a element.
1631 void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)
1633 if (!mChildren[side].removeOne(element))
1634 qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1638 ////////////////////////////////////////////////////////////////////////////////////////////////////
1639 //////////////////// QCPLayoutElement
1640 ////////////////////////////////////////////////////////////////////////////////////////////////////
1642 /*! \class QCPLayoutElement
1643 \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system".
1645 This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.
1647 A Layout element is a rectangular object which can be placed in layouts. It has an outer rect
1648 (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference
1649 between outer and inner rect is called its margin. The margin can either be set to automatic or
1650 manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be
1651 set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic,
1652 the layout element subclass will control the value itself (via \ref calculateAutoMargin).
1654 Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level
1655 layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref
1656 QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested.
1658 Thus in QCustomPlot one can divide layout elements into two categories: The ones that are
1659 invisible by themselves, because they don't draw anything. Their only purpose is to manage the
1660 position and size of other layout elements. This category of layout elements usually use
1661 QCPLayout as base class. Then there is the category of layout elements which actually draw
1662 something. For example, QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does
1663 not necessarily mean that the latter category can't have child layout elements. QCPLegend for
1664 instance, actually derives from QCPLayoutGrid and the individual legend items are child layout
1665 elements in the grid layout.
1668 /* start documentation of inline functions */
1670 /*! \fn QCPLayout *QCPLayoutElement::layout() const
1672 Returns the parent layout of this layout element.
1675 /*! \fn QRect QCPLayoutElement::rect() const
1677 Returns the inner rect of this layout element. The inner rect is the outer rect (\ref
1678 setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins).
1680 In some cases, the area between outer and inner rect is left blank. In other cases the margin
1681 area is used to display peripheral graphics while the main content is in the inner rect. This is
1682 where automatic margin calculation becomes interesting because it allows the layout element to
1683 adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
1684 draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if
1685 \ref setAutoMargins is enabled) according to the space required by the labels of the axes.
1688 /*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
1690 This event is called, if the mouse was pressed while being inside the outer rect of this layout
1694 /*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
1696 This event is called, if the mouse is moved inside the outer rect of this layout element.
1699 /*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
1701 This event is called, if the mouse was previously pressed inside the outer rect of this layout
1702 element and is now released.
1705 /*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
1707 This event is called, if the mouse is double-clicked inside the outer rect of this layout
1711 /*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
1713 This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this
1717 /* end documentation of inline functions */
1720 Creates an instance of QCPLayoutElement and sets default values.
1722 QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
1723 QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1726 mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1728 mOuterRect(0, 0, 0, 0),
1729 mMargins(0, 0, 0, 0),
1730 mMinimumMargins(0, 0, 0, 0),
1731 mAutoMargins(QCP::msAll)
1735 QCPLayoutElement::~QCPLayoutElement()
1737 setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1738 // unregister at layout:
1739 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
1740 mParentLayout->take(this);
1744 Sets the outer rect of this layout element. If the layout element is inside a layout, the layout
1745 sets the position and size of this layout element using this function.
1747 Calling this function externally has no effect, since the layout will overwrite any changes to
1748 the outer rect upon the next replot.
1750 The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect.
1754 void QCPLayoutElement::setOuterRect(const QRect &rect)
1756 if (mOuterRect != rect)
1759 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1764 Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all
1765 sides, this function is used to manually set the margin on those sides. Sides that are still set
1766 to be handled automatically are ignored and may have any value in \a margins.
1768 The margin is the distance between the outer rect (controlled by the parent layout via \ref
1769 setOuterRect) and the inner \ref rect (which usually contains the main content of this layout
1774 void QCPLayoutElement::setMargins(const QMargins &margins)
1776 if (mMargins != margins)
1779 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1784 If \ref setAutoMargins is enabled on some or all margins, this function is used to provide
1785 minimum values for those margins.
1787 The minimum values are not enforced on margin sides that were set to be under manual control via
1788 \ref setAutoMargins.
1792 void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1794 if (mMinimumMargins != margins)
1796 mMinimumMargins = margins;
1801 Sets on which sides the margin shall be calculated automatically. If a side is calculated
1802 automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is
1803 set to be controlled manually, the value may be specified with \ref setMargins.
1805 Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref
1806 setMarginGroup), to synchronize (align) it with other layout elements in the plot.
1808 \see setMinimumMargins, setMargins
1810 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1812 mAutoMargins = sides;
1816 Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to
1817 respect the \a size here by changing row/column sizes in the layout accordingly.
1819 If the parent layout size is not sufficient to satisfy all minimum size constraints of its child
1820 layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot
1821 propagates the layout's size constraints to the outside by setting its own minimum QWidget size
1822 accordingly, so violations of \a size should be exceptions.
1824 void QCPLayoutElement::setMinimumSize(const QSize &size)
1826 if (mMinimumSize != size)
1828 mMinimumSize = size;
1830 mParentLayout->sizeConstraintsChanged();
1836 Sets the minimum size for the inner \ref rect of this layout element.
1838 void QCPLayoutElement::setMinimumSize(int width, int height)
1840 setMinimumSize(QSize(width, height));
1844 Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to
1845 respect the \a size here by changing row/column sizes in the layout accordingly.
1847 void QCPLayoutElement::setMaximumSize(const QSize &size)
1849 if (mMaximumSize != size)
1851 mMaximumSize = size;
1853 mParentLayout->sizeConstraintsChanged();
1859 Sets the maximum size for the inner \ref rect of this layout element.
1861 void QCPLayoutElement::setMaximumSize(int width, int height)
1863 setMaximumSize(QSize(width, height));
1867 Sets the margin \a group of the specified margin \a sides.
1869 Margin groups allow synchronizing specified margins across layout elements, see the documentation
1870 of \ref QCPMarginGroup.
1872 To unset the margin group of \a sides, set \a group to 0.
1874 Note that margin groups only work for margin sides that are set to automatic (\ref
1877 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1879 QVector<QCP::MarginSide> sideVector;
1880 if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1881 if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1882 if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1883 if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1885 for (int i=0; i<sideVector.size(); ++i)
1887 QCP::MarginSide side = sideVector.at(i);
1888 if (marginGroup(side) != group)
1890 QCPMarginGroup *oldGroup = marginGroup(side);
1891 if (oldGroup) // unregister at old group
1892 oldGroup->removeChild(side, this);
1894 if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1896 mMarginGroups.remove(side);
1897 } else // setting to a new group
1899 mMarginGroups[side] = group;
1900 group->addChild(side, this);
1907 Updates the layout element and sub-elements. This function is automatically called before every
1908 replot by the parent layout element. It is called multiple times, once for every \ref
1909 UpdatePhase. The phases are run through in the order of the enum values. For details about what
1910 happens at the different phases, see the documentation of \ref UpdatePhase.
1912 Layout elements that have child elements should call the \ref update method of their child
1913 elements, and pass the current \a phase unchanged.
1915 The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
1916 Subclasses should make sure to call the base class implementation.
1918 void QCPLayoutElement::update(UpdatePhase phase)
1920 if (phase == upMargins)
1922 if (mAutoMargins != QCP::msNone)
1924 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1925 QMargins newMargins = mMargins;
1926 QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
1927 foreach (QCP::MarginSide side, allMarginSides)
1929 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1931 if (mMarginGroups.contains(side))
1932 QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1934 QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1935 // apply minimum margin restrictions:
1936 if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1937 QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1940 setMargins(newMargins);
1946 Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
1948 if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this
1949 function to determine the minimum allowed size of this layout element. (A manual minimum size is
1950 considered set if it is non-zero.)
1952 QSize QCPLayoutElement::minimumSizeHint() const
1954 return mMinimumSize;
1958 Returns the maximum size this layout element (the inner \ref rect) may be expanded to.
1960 if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this
1961 function to determine the maximum allowed size of this layout element. (A manual maximum size is
1962 considered set if it is smaller than Qt's QWIDGETSIZE_MAX.)
1964 QSize QCPLayoutElement::maximumSizeHint() const
1966 return mMaximumSize;
1970 Returns a list of all child elements in this layout element. If \a recursive is true, all
1971 sub-child elements are included in the list, too.
1973 \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
1974 empty cells which yield 0 at the respective index.)
1976 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
1979 return QList<QCPLayoutElement*>();
1983 Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer
1984 rect, this method returns a value corresponding to 0.99 times the parent plot's selection
1985 tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is
1986 true, -1.0 is returned.
1988 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
1990 QCPLayoutElement subclasses may reimplement this method to provide more specific selection test
1993 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2000 if (QRectF(mOuterRect).contains(pos))
2003 return mParentPlot->selectionTolerance()*0.99;
2006 qDebug() << Q_FUNC_INFO << "parent plot not defined";
2015 propagates the parent plot initialization to all child elements, by calling \ref
2016 QCPLayerable::initializeParentPlot on them.
2018 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2020 foreach (QCPLayoutElement* el, elements(false))
2022 if (!el->parentPlot())
2023 el->initializeParentPlot(parentPlot);
2029 Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
2030 side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
2031 returned value will not be smaller than the specified minimum margin.
2033 The default implementation just returns the respective manual margin (\ref setMargins) or the
2034 minimum margin, whichever is larger.
2036 int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)
2038 return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
2041 ////////////////////////////////////////////////////////////////////////////////////////////////////
2042 //////////////////// QCPLayout
2043 ////////////////////////////////////////////////////////////////////////////////////////////////////
2045 /*! \class QCPLayout
2046 \brief The abstract base class for layouts
2048 This is an abstract base class for layout elements whose main purpose is to define the position
2049 and size of other child layout elements. In most cases, layouts don't draw anything themselves
2050 (but there are exceptions to this, e.g. QCPLegend).
2052 QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.
2054 QCPLayout introduces a common interface for accessing and manipulating the child elements. Those
2055 functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref
2056 simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions
2057 to this interface which are more specialized to the form of the layout. For example, \ref
2058 QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid
2061 Since this is an abstract base class, you can't instantiate it directly. Rather use one of its
2062 subclasses like QCPLayoutGrid or QCPLayoutInset.
2064 For a general introduction to the layout system, see the dedicated documentation page \ref
2065 thelayoutsystem "The Layout System".
2068 /* start documentation of pure virtual functions */
2070 /*! \fn virtual int QCPLayout::elementCount() const = 0
2072 Returns the number of elements/cells in the layout.
2074 \see elements, elementAt
2077 /*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
2079 Returns the element in the cell with the given \a index. If \a index is invalid, returns 0.
2081 Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
2082 QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check
2083 whether a cell is empty or not.
2085 \see elements, elementCount, takeAt
2088 /*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
2090 Removes the element with the given \a index from the layout and returns it.
2092 If the \a index is invalid or the cell with that index is empty, returns 0.
2094 Note that some layouts don't remove the respective cell right away but leave an empty cell after
2095 successful removal of the layout element. To collapse empty cells, use \ref simplify.
2097 \see elementAt, take
2100 /*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
2102 Removes the specified \a element from the layout and returns true on success.
2104 If the \a element isn't in this layout, returns false.
2106 Note that some layouts don't remove the respective cell right away but leave an empty cell after
2107 successful removal of the layout element. To collapse empty cells, use \ref simplify.
2112 /* end documentation of pure virtual functions */
2115 Creates an instance of QCPLayout and sets default values. Note that since QCPLayout
2116 is an abstract base class, it can't be instantiated directly.
2118 QCPLayout::QCPLayout()
2123 First calls the QCPLayoutElement::update base class implementation to update the margins on this
2126 Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells.
2128 Finally, \ref update is called on all child elements.
2130 void QCPLayout::update(UpdatePhase phase)
2132 QCPLayoutElement::update(phase);
2134 // set child element rects according to layout:
2135 if (phase == upLayout)
2138 // propagate update call to child elements:
2139 const int elCount = elementCount();
2140 for (int i=0; i<elCount; ++i)
2142 if (QCPLayoutElement *el = elementAt(i))
2147 /* inherits documentation from base class */
2148 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2150 const int c = elementCount();
2151 QList<QCPLayoutElement*> result;
2152 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2155 for (int i=0; i<c; ++i)
2156 result.append(elementAt(i));
2159 for (int i=0; i<c; ++i)
2162 result << result.at(i)->elements(recursive);
2169 Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the
2170 default implementation does nothing.
2172 Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit
2173 simplification while QCPLayoutGrid does.
2175 void QCPLayout::simplify()
2180 Removes and deletes the element at the provided \a index. Returns true on success. If \a index is
2181 invalid or points to an empty cell, returns false.
2183 This function internally uses \ref takeAt to remove the element from the layout and then deletes
2184 the returned element. Note that some layouts don't remove the respective cell right away but leave an
2185 empty cell after successful removal of the layout element. To collapse empty cells, use \ref
2190 bool QCPLayout::removeAt(int index)
2192 if (QCPLayoutElement *el = takeAt(index))
2201 Removes and deletes the provided \a element. Returns true on success. If \a element is not in the
2202 layout, returns false.
2204 This function internally uses \ref takeAt to remove the element from the layout and then deletes
2205 the element. Note that some layouts don't remove the respective cell right away but leave an
2206 empty cell after successful removal of the layout element. To collapse empty cells, use \ref
2211 bool QCPLayout::remove(QCPLayoutElement *element)
2222 Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure
2223 all empty cells are collapsed.
2225 \see remove, removeAt
2227 void QCPLayout::clear()
2229 for (int i=elementCount()-1; i>=0; --i)
2238 Subclasses call this method to report changed (minimum/maximum) size constraints.
2240 If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref
2241 sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of
2242 QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,
2243 it may update itself and resize cells accordingly.
2245 void QCPLayout::sizeConstraintsChanged() const
2247 if (QWidget *w = qobject_cast<QWidget*>(parent()))
2248 w->updateGeometry();
2249 else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2250 l->sizeConstraintsChanged();
2255 Subclasses reimplement this method to update the position and sizes of the child elements/cells
2256 via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing.
2258 The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay
2261 \ref getSectionSizes may help with the reimplementation of this function.
2265 void QCPLayout::updateLayout()
2272 Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the
2273 \ref QCPLayerable::parentLayerable and the QObject parent to this layout.
2275 Further, if \a el didn't previously have a parent plot, calls \ref
2276 QCPLayerable::initializeParentPlot on \a el to set the paret plot.
2278 This method is used by subclass specific methods that add elements to the layout. Note that this
2279 method only changes properties in \a el. The removal from the old layout and the insertion into
2280 the new layout must be done additionally.
2282 void QCPLayout::adoptElement(QCPLayoutElement *el)
2286 el->mParentLayout = this;
2287 el->setParentLayerable(this);
2288 el->setParent(this);
2289 if (!el->parentPlot())
2290 el->initializeParentPlot(mParentPlot);
2292 qDebug() << Q_FUNC_INFO << "Null element passed";
2297 Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout
2298 and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent
2301 This method is used by subclass specific methods that remove elements from the layout (e.g. \ref
2302 take or \ref takeAt). Note that this method only changes properties in \a el. The removal from
2303 the old layout must be done additionally.
2305 void QCPLayout::releaseElement(QCPLayoutElement *el)
2309 el->mParentLayout = 0;
2310 el->setParentLayerable(0);
2311 el->setParent(mParentPlot);
2312 // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2314 qDebug() << Q_FUNC_INFO << "Null element passed";
2319 This is a helper function for the implementation of \ref updateLayout in subclasses.
2321 It calculates the sizes of one-dimensional sections with provided constraints on maximum section
2322 sizes, minimum section sizes, relative stretch factors and the final total size of all sections.
2324 The QVector entries refer to the sections. Thus all QVectors must have the same size.
2326 \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size
2327 imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
2329 \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size
2330 imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than
2331 \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,
2332 not exceeding the allowed total size is taken to be more important than not going below minimum
2335 \a stretchFactors give the relative proportions of the sections to each other. If all sections
2336 shall be scaled equally, set all values equal. If the first section shall be double the size of
2337 each individual other section, set the first number of \a stretchFactors to double the value of
2338 the other individual values (e.g. {2, 1, 1, 1}).
2340 \a totalSize is the value that the final section sizes will add up to. Due to rounding, the
2341 actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,
2342 you could distribute the remaining difference on the sections.
2344 The return value is a QVector containing the section sizes.
2346 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2348 if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2350 qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2351 return QVector<int>();
2353 if (stretchFactors.isEmpty())
2354 return QVector<int>();
2355 int sectionCount = stretchFactors.size();
2356 QVector<double> sectionSizes(sectionCount);
2357 // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2359 for (int i=0; i<sectionCount; ++i)
2360 minSizeSum += minSizes.at(i);
2361 if (totalSize < minSizeSum)
2363 // new stretch factors are minimum sizes and minimum sizes are set to zero:
2364 for (int i=0; i<sectionCount; ++i)
2366 stretchFactors[i] = minSizes.at(i);
2371 QList<int> minimumLockedSections;
2372 QList<int> unfinishedSections;
2373 for (int i=0; i<sectionCount; ++i)
2374 unfinishedSections.append(i);
2375 double freeSize = totalSize;
2377 int outerIterations = 0;
2378 while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2381 int innerIterations = 0;
2382 while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2385 // find section that hits its maximum next:
2387 double nextMax = 1e12;
2388 for (int i=0; i<unfinishedSections.size(); ++i)
2390 int secId = unfinishedSections.at(i);
2391 double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2392 if (hitsMaxAt < nextMax)
2394 nextMax = hitsMaxAt;
2398 // 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
2399 // actually hits its maximum, without exceeding the total size when we add up all sections)
2400 double stretchFactorSum = 0;
2401 for (int i=0; i<unfinishedSections.size(); ++i)
2402 stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2403 double nextMaxLimit = freeSize/stretchFactorSum;
2404 if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2406 for (int i=0; i<unfinishedSections.size(); ++i)
2408 sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2409 freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2411 unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2412 } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2414 for (int i=0; i<unfinishedSections.size(); ++i)
2415 sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2416 unfinishedSections.clear();
2419 if (innerIterations == sectionCount*2)
2420 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2422 // now check whether the resulting section sizes violate minimum restrictions:
2423 bool foundMinimumViolation = false;
2424 for (int i=0; i<sectionSizes.size(); ++i)
2426 if (minimumLockedSections.contains(i))
2428 if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2430 sectionSizes[i] = minSizes.at(i); // set it to minimum
2431 foundMinimumViolation = true; // make sure we repeat the whole optimization process
2432 minimumLockedSections.append(i);
2435 if (foundMinimumViolation)
2437 freeSize = totalSize;
2438 for (int i=0; i<sectionCount; ++i)
2440 if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2441 unfinishedSections.append(i);
2443 freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2445 // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2446 for (int i=0; i<unfinishedSections.size(); ++i)
2447 sectionSizes[unfinishedSections.at(i)] = 0;
2450 if (outerIterations == sectionCount*2)
2451 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2453 QVector<int> result(sectionCount);
2454 for (int i=0; i<sectionCount; ++i)
2455 result[i] = qRound(sectionSizes.at(i));
2460 ////////////////////////////////////////////////////////////////////////////////////////////////////
2461 //////////////////// QCPLayoutGrid
2462 ////////////////////////////////////////////////////////////////////////////////////////////////////
2464 /*! \class QCPLayoutGrid
2465 \brief A layout that arranges child elements in a grid
2467 Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor,
2468 \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing).
2470 Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or
2471 column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref
2472 hasElement, that element can be retrieved with \ref element. If rows and columns that only have
2473 empty cells shall be removed, call \ref simplify. Removal of elements is either done by just
2474 adding the element to a different layout or by using the QCPLayout interface \ref take or \ref
2477 Row and column insertion can be performed with \ref insertRow and \ref insertColumn.
2481 Creates an instance of QCPLayoutGrid and sets default values.
2483 QCPLayoutGrid::QCPLayoutGrid() :
2489 QCPLayoutGrid::~QCPLayoutGrid()
2491 // clear all child layout elements. This is important because only the specific layouts know how
2492 // to handle removing elements (clear calls virtual removeAt method to do that).
2497 Returns the element in the cell in \a row and \a column.
2499 Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug
2500 message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
2502 \see addElement, hasElement
2504 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2506 if (row >= 0 && row < mElements.size())
2508 if (column >= 0 && column < mElements.first().size())
2510 if (QCPLayoutElement *result = mElements.at(row).at(column))
2513 qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2515 qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2517 qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2522 Returns the number of rows in the layout.
2526 int QCPLayoutGrid::rowCount() const
2528 return mElements.size();
2532 Returns the number of columns in the layout.
2536 int QCPLayoutGrid::columnCount() const
2538 if (mElements.size() > 0)
2539 return mElements.first().size();
2545 Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it
2546 is first removed from there. If \a row or \a column don't exist yet, the layout is expanded
2549 Returns true if the element was added successfully, i.e. if the cell at \a row and \a column
2550 didn't already have an element.
2552 \see element, hasElement, take, remove
2554 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2558 if (!hasElement(row, column))
2560 if (element->layout()) // remove from old layout first
2561 element->layout()->take(element);
2562 expandTo(row+1, column+1);
2563 mElements[row][column] = element;
2564 adoptElement(element);
2567 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2569 qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2574 Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't
2579 bool QCPLayoutGrid::hasElement(int row, int column)
2581 if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2582 return mElements.at(row).at(column);
2588 Sets the stretch \a factor of \a column.
2590 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2591 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2592 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2594 The default stretch factor of newly created rows/columns is 1.
2596 \see setColumnStretchFactors, setRowStretchFactor
2598 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2600 if (column >= 0 && column < columnCount())
2603 mColumnStretchFactors[column] = factor;
2605 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2607 qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2611 Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount.
2613 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2614 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2615 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2617 The default stretch factor of newly created rows/columns is 1.
2619 \see setColumnStretchFactor, setRowStretchFactors
2621 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2623 if (factors.size() == mColumnStretchFactors.size())
2625 mColumnStretchFactors = factors;
2626 for (int i=0; i<mColumnStretchFactors.size(); ++i)
2628 if (mColumnStretchFactors.at(i) <= 0)
2630 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2631 mColumnStretchFactors[i] = 1;
2635 qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2639 Sets the stretch \a factor of \a row.
2641 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2642 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2643 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2645 The default stretch factor of newly created rows/columns is 1.
2647 \see setColumnStretchFactors, setRowStretchFactor
2649 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2651 if (row >= 0 && row < rowCount())
2654 mRowStretchFactors[row] = factor;
2656 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2658 qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2662 Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount.
2664 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2665 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2666 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2668 The default stretch factor of newly created rows/columns is 1.
2670 \see setRowStretchFactor, setColumnStretchFactors
2672 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2674 if (factors.size() == mRowStretchFactors.size())
2676 mRowStretchFactors = factors;
2677 for (int i=0; i<mRowStretchFactors.size(); ++i)
2679 if (mRowStretchFactors.at(i) <= 0)
2681 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2682 mRowStretchFactors[i] = 1;
2686 qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2690 Sets the gap that is left blank between columns to \a pixels.
2694 void QCPLayoutGrid::setColumnSpacing(int pixels)
2696 mColumnSpacing = pixels;
2700 Sets the gap that is left blank between rows to \a pixels.
2702 \see setColumnSpacing
2704 void QCPLayoutGrid::setRowSpacing(int pixels)
2706 mRowSpacing = pixels;
2710 Expands the layout to have \a newRowCount rows and \a newColumnCount columns. So the last valid
2711 row index will be \a newRowCount-1, the last valid column index will be \a newColumnCount-1.
2713 If the current column/row count is already larger or equal to \a newColumnCount/\a newRowCount,
2714 this function does nothing in that dimension.
2716 Newly created cells are empty, new rows and columns have the stretch factor 1.
2718 Note that upon a call to \ref addElement, the layout is expanded automatically to contain the
2719 specified row and column, using this function.
2723 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2725 // add rows as necessary:
2726 while (rowCount() < newRowCount)
2728 mElements.append(QList<QCPLayoutElement*>());
2729 mRowStretchFactors.append(1);
2731 // go through rows and expand columns as necessary:
2732 int newColCount = qMax(columnCount(), newColumnCount);
2733 for (int i=0; i<rowCount(); ++i)
2735 while (mElements.at(i).size() < newColCount)
2736 mElements[i].append(0);
2738 while (mColumnStretchFactors.size() < newColCount)
2739 mColumnStretchFactors.append(1);
2743 Inserts a new row with empty cells at the row index \a newIndex. Valid values for \a newIndex
2744 range from 0 (inserts a row at the top) to \a rowCount (appends a row at the bottom).
2748 void QCPLayoutGrid::insertRow(int newIndex)
2750 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2758 if (newIndex > rowCount())
2759 newIndex = rowCount();
2761 mRowStretchFactors.insert(newIndex, 1);
2762 QList<QCPLayoutElement*> newRow;
2763 for (int col=0; col<columnCount(); ++col)
2764 newRow.append((QCPLayoutElement*)0);
2765 mElements.insert(newIndex, newRow);
2769 Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a
2770 newIndex range from 0 (inserts a row at the left) to \a rowCount (appends a row at the right).
2774 void QCPLayoutGrid::insertColumn(int newIndex)
2776 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2784 if (newIndex > columnCount())
2785 newIndex = columnCount();
2787 mColumnStretchFactors.insert(newIndex, 1);
2788 for (int row=0; row<rowCount(); ++row)
2789 mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2792 /* inherits documentation from base class */
2793 void QCPLayoutGrid::updateLayout()
2795 QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2796 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2797 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2799 int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2800 int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2801 QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2802 QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2804 // go through cells and set rects accordingly:
2805 int yOffset = mRect.top();
2806 for (int row=0; row<rowCount(); ++row)
2809 yOffset += rowHeights.at(row-1)+mRowSpacing;
2810 int xOffset = mRect.left();
2811 for (int col=0; col<columnCount(); ++col)
2814 xOffset += colWidths.at(col-1)+mColumnSpacing;
2815 if (mElements.at(row).at(col))
2816 mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2821 /* inherits documentation from base class */
2822 int QCPLayoutGrid::elementCount() const
2824 return rowCount()*columnCount();
2827 /* inherits documentation from base class */
2828 QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
2830 if (index >= 0 && index < elementCount())
2831 return mElements.at(index / columnCount()).at(index % columnCount());
2836 /* inherits documentation from base class */
2837 QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
2839 if (QCPLayoutElement *el = elementAt(index))
2842 mElements[index / columnCount()][index % columnCount()] = 0;
2846 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2851 /* inherits documentation from base class */
2852 bool QCPLayoutGrid::take(QCPLayoutElement *element)
2856 for (int i=0; i<elementCount(); ++i)
2858 if (elementAt(i) == element)
2864 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2866 qDebug() << Q_FUNC_INFO << "Can't take null element";
2870 /* inherits documentation from base class */
2871 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2873 QList<QCPLayoutElement*> result;
2874 int colC = columnCount();
2875 int rowC = rowCount();
2876 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2877 result.reserve(colC*rowC);
2879 for (int row=0; row<rowC; ++row)
2881 for (int col=0; col<colC; ++col)
2883 result.append(mElements.at(row).at(col));
2888 int c = result.size();
2889 for (int i=0; i<c; ++i)
2892 result << result.at(i)->elements(recursive);
2899 Simplifies the layout by collapsing rows and columns which only contain empty cells.
2901 void QCPLayoutGrid::simplify()
2903 // remove rows with only empty cells:
2904 for (int row=rowCount()-1; row>=0; --row)
2906 bool hasElements = false;
2907 for (int col=0; col<columnCount(); ++col)
2909 if (mElements.at(row).at(col))
2917 mRowStretchFactors.removeAt(row);
2918 mElements.removeAt(row);
2919 if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2920 mColumnStretchFactors.clear();
2924 // remove columns with only empty cells:
2925 for (int col=columnCount()-1; col>=0; --col)
2927 bool hasElements = false;
2928 for (int row=0; row<rowCount(); ++row)
2930 if (mElements.at(row).at(col))
2938 mColumnStretchFactors.removeAt(col);
2939 for (int row=0; row<rowCount(); ++row)
2940 mElements[row].removeAt(col);
2945 /* inherits documentation from base class */
2946 QSize QCPLayoutGrid::minimumSizeHint() const
2948 QVector<int> minColWidths, minRowHeights;
2949 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2951 for (int i=0; i<minColWidths.size(); ++i)
2952 result.rwidth() += minColWidths.at(i);
2953 for (int i=0; i<minRowHeights.size(); ++i)
2954 result.rheight() += minRowHeights.at(i);
2955 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2956 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2960 /* inherits documentation from base class */
2961 QSize QCPLayoutGrid::maximumSizeHint() const
2963 QVector<int> maxColWidths, maxRowHeights;
2964 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2967 for (int i=0; i<maxColWidths.size(); ++i)
2968 result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2969 for (int i=0; i<maxRowHeights.size(); ++i)
2970 result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2971 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2972 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2978 Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights
2981 The minimum height of a row is the largest minimum height of any element in that row. The minimum
2982 width of a column is the largest minimum width of any element in that column.
2984 This is a helper function for \ref updateLayout.
2986 \see getMaximumRowColSizes
2988 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
2990 *minColWidths = QVector<int>(columnCount(), 0);
2991 *minRowHeights = QVector<int>(rowCount(), 0);
2992 for (int row=0; row<rowCount(); ++row)
2994 for (int col=0; col<columnCount(); ++col)
2996 if (mElements.at(row).at(col))
2998 QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
2999 QSize min = mElements.at(row).at(col)->minimumSize();
3000 QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3001 if (minColWidths->at(col) < final.width())
3002 (*minColWidths)[col] = final.width();
3003 if (minRowHeights->at(row) < final.height())
3004 (*minRowHeights)[row] = final.height();
3012 Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights
3015 The maximum height of a row is the smallest maximum height of any element in that row. The
3016 maximum width of a column is the smallest maximum width of any element in that column.
3018 This is a helper function for \ref updateLayout.
3020 \see getMinimumRowColSizes
3022 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3024 *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3025 *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3026 for (int row=0; row<rowCount(); ++row)
3028 for (int col=0; col<columnCount(); ++col)
3030 if (mElements.at(row).at(col))
3032 QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3033 QSize max = mElements.at(row).at(col)->maximumSize();
3034 QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3035 if (maxColWidths->at(col) > final.width())
3036 (*maxColWidths)[col] = final.width();
3037 if (maxRowHeights->at(row) > final.height())
3038 (*maxRowHeights)[row] = final.height();
3045 ////////////////////////////////////////////////////////////////////////////////////////////////////
3046 //////////////////// QCPLayoutInset
3047 ////////////////////////////////////////////////////////////////////////////////////////////////////
3048 /*! \class QCPLayoutInset
3049 \brief A layout that places child elements aligned to the border or arbitrarily positioned
3051 Elements are placed either aligned to the border or at arbitrary position in the area of the
3052 layout. Which placement applies is controlled with the \ref InsetPlacement (\ref
3055 Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or
3056 addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset
3057 placement will default to \ref ipBorderAligned and the element will be aligned according to the
3058 \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at
3059 arbitrary position and size, defined by \a rect.
3061 The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively.
3063 This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout.
3066 /* start documentation of inline functions */
3068 /*! \fn virtual void QCPLayoutInset::simplify()
3070 The QCPInsetLayout does not need simplification since it can never have empty cells due to its
3071 linear index structure. This method does nothing.
3074 /* end documentation of inline functions */
3077 Creates an instance of QCPLayoutInset and sets default values.
3079 QCPLayoutInset::QCPLayoutInset()
3083 QCPLayoutInset::~QCPLayoutInset()
3085 // clear all child layout elements. This is important because only the specific layouts know how
3086 // to handle removing elements (clear calls virtual removeAt method to do that).
3091 Returns the placement type of the element with the specified \a index.
3093 QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const
3095 if (elementAt(index))
3096 return mInsetPlacement.at(index);
3099 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3105 Returns the alignment of the element with the specified \a index. The alignment only has a
3106 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned.
3108 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3110 if (elementAt(index))
3111 return mInsetAlignment.at(index);
3114 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3120 Returns the rect of the element with the specified \a index. The rect only has a
3121 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
3123 QRectF QCPLayoutInset::insetRect(int index) const
3125 if (elementAt(index))
3126 return mInsetRect.at(index);
3129 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3135 Sets the inset placement type of the element with the specified \a index to \a placement.
3139 void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)
3141 if (elementAt(index))
3142 mInsetPlacement[index] = placement;
3144 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3148 If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function
3149 is used to set the alignment of the element with the specified \a index to \a alignment.
3151 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3152 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3153 alignment flags will be ignored.
3155 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3157 if (elementAt(index))
3158 mInsetAlignment[index] = alignment;
3160 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3164 If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the
3165 position and size of the element with the specified \a index to \a rect.
3167 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3168 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3169 corner of the layout, with 35% width and height of the parent layout.
3171 Note that the minimum and maximum sizes of the embedded element (\ref
3172 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced.
3174 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3176 if (elementAt(index))
3177 mInsetRect[index] = rect;
3179 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3182 /* inherits documentation from base class */
3183 void QCPLayoutInset::updateLayout()
3185 for (int i=0; i<mElements.size(); ++i)
3188 QSize finalMinSize, finalMaxSize;
3189 QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3190 QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3191 finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3192 finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3193 finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3194 finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3195 if (mInsetPlacement.at(i) == ipFree)
3197 insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3198 rect().y()+rect().height()*mInsetRect.at(i).y(),
3199 rect().width()*mInsetRect.at(i).width(),
3200 rect().height()*mInsetRect.at(i).height());
3201 if (insetRect.size().width() < finalMinSize.width())
3202 insetRect.setWidth(finalMinSize.width());
3203 if (insetRect.size().height() < finalMinSize.height())
3204 insetRect.setHeight(finalMinSize.height());
3205 if (insetRect.size().width() > finalMaxSize.width())
3206 insetRect.setWidth(finalMaxSize.width());
3207 if (insetRect.size().height() > finalMaxSize.height())
3208 insetRect.setHeight(finalMaxSize.height());
3209 } else if (mInsetPlacement.at(i) == ipBorderAligned)
3211 insetRect.setSize(finalMinSize);
3212 Qt::Alignment al = mInsetAlignment.at(i);
3213 if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3214 else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3215 else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3216 if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3217 else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3218 else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3220 mElements.at(i)->setOuterRect(insetRect);
3224 /* inherits documentation from base class */
3225 int QCPLayoutInset::elementCount() const
3227 return mElements.size();
3230 /* inherits documentation from base class */
3231 QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
3233 if (index >= 0 && index < mElements.size())
3234 return mElements.at(index);
3239 /* inherits documentation from base class */
3240 QCPLayoutElement *QCPLayoutInset::takeAt(int index)
3242 if (QCPLayoutElement *el = elementAt(index))
3245 mElements.removeAt(index);
3246 mInsetPlacement.removeAt(index);
3247 mInsetAlignment.removeAt(index);
3248 mInsetRect.removeAt(index);
3252 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3257 /* inherits documentation from base class */
3258 bool QCPLayoutInset::take(QCPLayoutElement *element)
3262 for (int i=0; i<elementCount(); ++i)
3264 if (elementAt(i) == element)
3270 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3272 qDebug() << Q_FUNC_INFO << "Can't take null element";
3277 The inset layout is sensitive to events only at areas where its (visible) child elements are
3278 sensitive. If the selectTest method of any of the child elements returns a positive number for \a
3279 pos, this method returns a value corresponding to 0.99 times the parent plot's selection
3280 tolerance. The inset layout is not selectable itself by default. So if \a onlySelectable is true,
3283 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
3285 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3291 for (int i=0; i<mElements.size(); ++i)
3293 // inset layout shall only return positive selectTest, if actually an inset object is at pos
3294 // else it would block the entire underlying QCPAxisRect with its surface.
3295 if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3296 return mParentPlot->selectionTolerance()*0.99;
3302 Adds the specified \a element to the layout as an inset aligned at the border (\ref
3303 setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a
3306 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3307 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3308 alignment flags will be ignored.
3310 \see addElement(QCPLayoutElement *element, const QRectF &rect)
3312 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3316 if (element->layout()) // remove from old layout first
3317 element->layout()->take(element);
3318 mElements.append(element);
3319 mInsetPlacement.append(ipBorderAligned);
3320 mInsetAlignment.append(alignment);
3321 mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3322 adoptElement(element);
3324 qDebug() << Q_FUNC_INFO << "Can't add null element";
3328 Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref
3329 setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a
3332 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3333 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3334 corner of the layout, with 35% width and height of the parent layout.
3336 \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3338 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3342 if (element->layout()) // remove from old layout first
3343 element->layout()->take(element);
3344 mElements.append(element);
3345 mInsetPlacement.append(ipFree);
3346 mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3347 mInsetRect.append(rect);
3348 adoptElement(element);
3350 qDebug() << Q_FUNC_INFO << "Can't add null element";
3354 ////////////////////////////////////////////////////////////////////////////////////////////////////
3355 //////////////////// QCPLineEnding
3356 ////////////////////////////////////////////////////////////////////////////////////////////////////
3358 /*! \class QCPLineEnding
3359 \brief Handles the different ending decorations for line-like items
3361 \image html QCPLineEnding.png "The various ending styles currently supported"
3363 For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
3364 has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
3366 The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
3367 be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
3368 the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
3369 For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
3370 directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
3371 respective arrow point inward.
3373 Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
3374 QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g.
3375 \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead
3379 Creates a QCPLineEnding instance with default values (style \ref esNone).
3381 QCPLineEnding::QCPLineEnding() :
3390 Creates a QCPLineEnding instance with the specified values.
3392 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3401 Sets the style of the ending decoration.
3403 void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
3409 Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
3410 width defines the size perpendicular to the arrow's pointing direction.
3414 void QCPLineEnding::setWidth(double width)
3420 Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
3421 length defines the size in pointing direction.
3425 void QCPLineEnding::setLength(double length)
3431 Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point
3432 inward when \a inverted is set to true.
3434 Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or
3435 discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are
3436 affected by it, which can be used to control to which side the half bar points to.
3438 void QCPLineEnding::setInverted(bool inverted)
3440 mInverted = inverted;
3445 Returns the maximum pixel radius the ending decoration might cover, starting from the position
3446 the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
3448 This is relevant for clipping. Only omit painting of the decoration when the position where the
3449 decoration is supposed to be drawn is farther away from the clipping rect than the returned
3452 double QCPLineEnding::boundingDistance() const
3463 return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3470 return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3477 Starting from the origin of this line ending (which is style specific), returns the length
3478 covered by the line ending symbol, in backward direction.
3480 For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if
3481 both have the same \ref setLength value, because the spike arrow has an inward curved back, which
3482 reduces the length along its center axis (the drawing origin for arrows is at the tip).
3484 This function is used for precise, style specific placement of line endings, for example in
3487 double QCPLineEnding::realLength() const
3514 Draws the line ending with the specified \a painter at the position \a pos. The direction of the
3515 line ending is controlled with \a dir.
3517 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3519 if (mStyle == esNone)
3522 QVector2D lengthVec(dir.normalized());
3523 if (lengthVec.isNull())
3524 lengthVec = QVector2D(1, 0);
3525 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3526 lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3527 widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3529 QPen penBackup = painter->pen();
3530 QBrush brushBackup = painter->brush();
3531 QPen miterPen = penBackup;
3532 miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3533 QBrush brush(painter->pen().color(), Qt::SolidPattern);
3539 QPointF points[3] = {pos.toPointF(),
3540 (pos-lengthVec+widthVec).toPointF(),
3541 (pos-lengthVec-widthVec).toPointF()
3543 painter->setPen(miterPen);
3544 painter->setBrush(brush);
3545 painter->drawConvexPolygon(points, 3);
3546 painter->setBrush(brushBackup);
3547 painter->setPen(penBackup);
3552 QPointF points[4] = {pos.toPointF(),
3553 (pos-lengthVec+widthVec).toPointF(),
3554 (pos-lengthVec*0.8f).toPointF(),
3555 (pos-lengthVec-widthVec).toPointF()
3557 painter->setPen(miterPen);
3558 painter->setBrush(brush);
3559 painter->drawConvexPolygon(points, 4);
3560 painter->setBrush(brushBackup);
3561 painter->setPen(penBackup);
3566 QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3568 (pos-lengthVec-widthVec).toPointF()
3570 painter->setPen(miterPen);
3571 painter->drawPolyline(points, 3);
3572 painter->setPen(penBackup);
3577 painter->setBrush(brush);
3578 painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3579 painter->setBrush(brushBackup);
3584 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3585 QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3586 (pos-widthVecPerp-widthVec).toPointF(),
3587 (pos+widthVecPerp-widthVec).toPointF(),
3588 (pos+widthVecPerp+widthVec).toPointF()
3590 painter->setPen(miterPen);
3591 painter->setBrush(brush);
3592 painter->drawConvexPolygon(points, 4);
3593 painter->setBrush(brushBackup);
3594 painter->setPen(penBackup);
3599 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3600 QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3601 (pos-widthVec).toPointF(),
3602 (pos+widthVecPerp).toPointF(),
3603 (pos+widthVec).toPointF()
3605 painter->setPen(miterPen);
3606 painter->setBrush(brush);
3607 painter->drawConvexPolygon(points, 4);
3608 painter->setBrush(brushBackup);
3609 painter->setPen(penBackup);
3614 painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3619 painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3624 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3626 // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3627 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3628 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3631 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3632 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3633 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3643 Draws the line ending. The direction is controlled with the \a angle parameter in radians.
3645 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3647 draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3651 ////////////////////////////////////////////////////////////////////////////////////////////////////
3652 //////////////////// QCPGrid
3653 ////////////////////////////////////////////////////////////////////////////////////////////////////
3656 \brief Responsible for drawing the grid of a QCPAxis.
3658 This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the
3659 grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref
3660 QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself.
3662 The axis and grid drawing was split into two classes to allow them to be placed on different
3663 layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid
3664 in the background and the axes in the foreground, and any plottables/items in between. This
3665 described situation is the default setup, see the QCPLayer documentation.
3669 Creates a QCPGrid instance and sets default values.
3671 You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid.
3673 QCPGrid::QCPGrid(QCPAxis *parentAxis) :
3674 QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3675 mParentAxis(parentAxis)
3677 // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3678 setParent(parentAxis);
3679 setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3680 setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3681 setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3682 setSubGridVisible(false);
3683 setAntialiased(false);
3684 setAntialiasedSubGrid(false);
3685 setAntialiasedZeroLine(false);
3689 Sets whether grid lines at sub tick marks are drawn.
3693 void QCPGrid::setSubGridVisible(bool visible)
3695 mSubGridVisible = visible;
3699 Sets whether sub grid lines are drawn antialiased.
3701 void QCPGrid::setAntialiasedSubGrid(bool enabled)
3703 mAntialiasedSubGrid = enabled;
3707 Sets whether zero lines are drawn antialiased.
3709 void QCPGrid::setAntialiasedZeroLine(bool enabled)
3711 mAntialiasedZeroLine = enabled;
3715 Sets the pen with which (major) grid lines are drawn.
3717 void QCPGrid::setPen(const QPen &pen)
3723 Sets the pen with which sub grid lines are drawn.
3725 void QCPGrid::setSubGridPen(const QPen &pen)
3731 Sets the pen with which zero lines are drawn.
3733 Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid
3734 lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
3736 void QCPGrid::setZeroLinePen(const QPen &pen)
3743 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
3744 before drawing the major grid lines.
3746 This is the antialiasing state the painter passed to the \ref draw method is in by default.
3748 This function takes into account the local setting of the antialiasing flag as well as the
3749 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
3750 QCustomPlot::setNotAntialiasedElements.
3754 void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
3756 applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
3761 Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
3762 over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
3764 void QCPGrid::draw(QCPPainter *painter)
3766 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3768 if (mSubGridVisible)
3769 drawSubGridLines(painter);
3770 drawGridLines(painter);
3775 Draws the main grid lines and possibly a zero line with the specified painter.
3777 This is a helper function called by \ref draw.
3779 void QCPGrid::drawGridLines(QCPPainter *painter) const
3781 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3783 int lowTick = mParentAxis->mLowestVisibleTick;
3784 int highTick = mParentAxis->mHighestVisibleTick;
3785 double t; // helper variable, result of coordinate-to-pixel transforms
3786 if (mParentAxis->orientation() == Qt::Horizontal)
3789 int zeroLineIndex = -1;
3790 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3792 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3793 painter->setPen(mZeroLinePen);
3794 double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3795 for (int i=lowTick; i <= highTick; ++i)
3797 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3800 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3801 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3807 applyDefaultAntialiasingHint(painter);
3808 painter->setPen(mPen);
3809 for (int i=lowTick; i <= highTick; ++i)
3811 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3812 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3813 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3818 int zeroLineIndex = -1;
3819 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3821 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3822 painter->setPen(mZeroLinePen);
3823 double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3824 for (int i=lowTick; i <= highTick; ++i)
3826 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3829 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3830 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3836 applyDefaultAntialiasingHint(painter);
3837 painter->setPen(mPen);
3838 for (int i=lowTick; i <= highTick; ++i)
3840 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3841 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3842 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3849 Draws the sub grid lines with the specified painter.
3851 This is a helper function called by \ref draw.
3853 void QCPGrid::drawSubGridLines(QCPPainter *painter) const
3855 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3857 applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
3858 double t; // helper variable, result of coordinate-to-pixel transforms
3859 painter->setPen(mSubGridPen);
3860 if (mParentAxis->orientation() == Qt::Horizontal)
3862 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3864 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
3865 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3869 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3871 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
3872 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3878 ////////////////////////////////////////////////////////////////////////////////////////////////////
3879 //////////////////// QCPAxis
3880 ////////////////////////////////////////////////////////////////////////////////////////////////////
3883 \brief Manages a single axis inside a QCustomPlot.
3885 Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via
3886 QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
3887 QCustomPlot::yAxis2 (right).
3889 Axes are always part of an axis rect, see QCPAxisRect.
3890 \image html AxisNamesOverview.png
3891 <center>Naming convention of axis parts</center>
3894 \image html AxisRectSpacingOverview.png
3895 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line
3896 on the left represents the QCustomPlot widget border.</center>
3900 /* start of documentation of inline functions */
3902 /*! \fn Qt::Orientation QCPAxis::orientation() const
3904 Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
3905 from the axis type (left, top, right or bottom).
3907 \see orientation(AxisType type)
3910 /*! \fn QCPGrid *QCPAxis::grid() const
3912 Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the
3916 /*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
3918 Returns the orientation of the specified axis type
3923 /* end of documentation of inline functions */
3924 /* start of documentation of signals */
3926 /*! \fn void QCPAxis::ticksRequest()
3928 This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
3929 labels for a replot.
3931 Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
3932 tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
3933 setTickVectorLabels.
3935 If you only want static ticks you probably don't need this signal, since you can just set the
3936 tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
3937 maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
3938 signal and set the vector/vectors there.
3941 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
3943 This signal is emitted when the range of this axis has changed. You can connect it to the \ref
3944 setRange slot of another axis to communicate the new range to the other axis, in order for it to
3947 You may also manipulate/correct the range with \ref setRange in a slot connected to this signal.
3948 This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper
3949 range shouldn't go beyond certain values. For example, the following slot would limit the x axis
3950 to only positive ranges:
3952 if (newRange.lower < 0)
3953 plot->xAxis->setRange(0, newRange.size());
3957 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
3960 Additionally to the new range, this signal also provides the previous range held by the axis as
3964 /*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
3966 This signal is emitted when the scale type changes, by calls to \ref setScaleType
3969 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
3971 This signal is emitted when the selection state of this axis has changed, either by user interaction
3972 or by a direct call to \ref setSelectedParts.
3975 /*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
3977 This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
3980 /* end of documentation of signals */
3983 Constructs an Axis instance of Type \a type for the axis rect \a parent.
3985 Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create
3986 them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,
3987 create them manually and then inject them also via \ref QCPAxisRect::addAxis.
3989 QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
3990 QCPLayerable(parent->parentPlot(), QString(), parent),
3995 mOrientation(orientation(type)),
3996 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
3997 mSelectedParts(spNone),
3998 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
3999 mSelectedBasePen(QPen(Qt::blue, 2)),
4002 mLabelFont(mParentPlot->font()),
4003 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4004 mLabelColor(Qt::black),
4005 mSelectedLabelColor(Qt::blue),
4008 mAutoTickLabels(true),
4009 mTickLabelType(ltNumber),
4010 mTickLabelFont(mParentPlot->font()),
4011 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4012 mTickLabelColor(Qt::black),
4013 mSelectedTickLabelColor(Qt::blue),
4014 mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4015 mDateTimeSpec(Qt::LocalTime),
4016 mNumberPrecision(6),
4017 mNumberFormatChar('g'),
4018 mNumberBeautifulPowers(true),
4019 // ticks and subticks:
4025 mAutoTickStep(true),
4026 mAutoSubTicks(true),
4027 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4028 mSelectedTickPen(QPen(Qt::blue, 2)),
4029 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4030 mSelectedSubTickPen(QPen(Qt::blue, 2)),
4033 mRangeReversed(false),
4034 mScaleType(stLinear),
4036 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4037 // internal members:
4038 mGrid(new QCPGrid(this)),
4039 mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4040 mLowestVisibleTick(0),
4041 mHighestVisibleTick(-1),
4042 mCachedMarginValid(false),
4046 mGrid->setVisible(false);
4047 setAntialiased(false);
4048 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
4052 setTickLabelPadding(3);
4054 } else if (type == atRight)
4056 setTickLabelPadding(7);
4057 setLabelPadding(12);
4058 } else if (type == atBottom)
4060 setTickLabelPadding(3);
4062 } else if (type == atLeft)
4064 setTickLabelPadding(5);
4065 setLabelPadding(10);
4071 delete mAxisPainter;
4072 delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
4075 /* No documentation as it is a property getter */
4076 int QCPAxis::tickLabelPadding() const
4078 return mAxisPainter->tickLabelPadding;
4081 /* No documentation as it is a property getter */
4082 double QCPAxis::tickLabelRotation() const
4084 return mAxisPainter->tickLabelRotation;
4087 /* No documentation as it is a property getter */
4088 QCPAxis::LabelSide QCPAxis::tickLabelSide() const
4090 return mAxisPainter->tickLabelSide;
4093 /* No documentation as it is a property getter */
4094 QString QCPAxis::numberFormat() const
4097 result.append(mNumberFormatChar);
4098 if (mNumberBeautifulPowers)
4100 result.append(QLatin1Char('b'));
4101 if (mAxisPainter->numberMultiplyCross)
4102 result.append(QLatin1Char('c'));
4107 /* No documentation as it is a property getter */
4108 int QCPAxis::tickLengthIn() const
4110 return mAxisPainter->tickLengthIn;
4113 /* No documentation as it is a property getter */
4114 int QCPAxis::tickLengthOut() const
4116 return mAxisPainter->tickLengthOut;
4119 /* No documentation as it is a property getter */
4120 int QCPAxis::subTickLengthIn() const
4122 return mAxisPainter->subTickLengthIn;
4125 /* No documentation as it is a property getter */
4126 int QCPAxis::subTickLengthOut() const
4128 return mAxisPainter->subTickLengthOut;
4131 /* No documentation as it is a property getter */
4132 int QCPAxis::labelPadding() const
4134 return mAxisPainter->labelPadding;
4137 /* No documentation as it is a property getter */
4138 int QCPAxis::offset() const
4140 return mAxisPainter->offset;
4143 /* No documentation as it is a property getter */
4144 QCPLineEnding QCPAxis::lowerEnding() const
4146 return mAxisPainter->lowerEnding;
4149 /* No documentation as it is a property getter */
4150 QCPLineEnding QCPAxis::upperEnding() const
4152 return mAxisPainter->upperEnding;
4156 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4157 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
4158 scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
4159 (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
4160 ticks, consider choosing a logarithm base of 100, 1000 or even higher.
4162 If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
4163 (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4164 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4165 part). To only display the decimal power, set the number precision to zero with
4166 \ref setNumberPrecision.
4168 void QCPAxis::setScaleType(QCPAxis::ScaleType type)
4170 if (mScaleType != type)
4173 if (mScaleType == stLogarithmic)
4174 setRange(mRange.sanitizedForLogScale());
4175 mCachedMarginValid = false;
4176 emit scaleTypeChanged(mScaleType);
4181 If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
4182 scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
4184 Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
4185 less major ticks, consider choosing \a base 100, 1000 or even higher.
4187 void QCPAxis::setScaleLogBase(double base)
4191 mScaleLogBase = base;
4192 mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4193 mCachedMarginValid = false;
4195 qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4199 Sets the range of the axis.
4201 This slot may be connected with the \ref rangeChanged signal of another axis so this axis
4202 is always synchronized with the other axis range, when it changes.
4204 To invert the direction of an axis, use \ref setRangeReversed.
4206 void QCPAxis::setRange(const QCPRange &range)
4208 if (range.lower == mRange.lower && range.upper == mRange.upper)
4211 if (!QCPRange::validRange(range)) return;
4212 QCPRange oldRange = mRange;
4213 if (mScaleType == stLogarithmic)
4215 mRange = range.sanitizedForLogScale();
4218 mRange = range.sanitizedForLinScale();
4220 mCachedMarginValid = false;
4221 emit rangeChanged(mRange);
4222 emit rangeChanged(mRange, oldRange);
4226 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
4227 (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
4229 However, even when \a selectable is set to a value not allowing the selection of a specific part,
4230 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
4233 \see SelectablePart, setSelectedParts
4235 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4237 if (mSelectableParts != selectable)
4239 mSelectableParts = selectable;
4240 emit selectableChanged(mSelectableParts);
4245 Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
4246 is selected, it uses a different pen/font.
4248 The entire selection mechanism for axes is handled automatically when \ref
4249 QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
4250 wish to change the selection state manually.
4252 This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
4254 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
4256 \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
4257 setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
4259 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4261 if (mSelectedParts != selected)
4263 mSelectedParts = selected;
4264 emit selectionChanged(mSelectedParts);
4271 Sets the lower and upper bound of the axis range.
4273 To invert the direction of an axis, use \ref setRangeReversed.
4275 There is also a slot to set a range, see \ref setRange(const QCPRange &range).
4277 void QCPAxis::setRange(double lower, double upper)
4279 if (lower == mRange.lower && upper == mRange.upper)
4282 if (!QCPRange::validRange(lower, upper)) return;
4283 QCPRange oldRange = mRange;
4284 mRange.lower = lower;
4285 mRange.upper = upper;
4286 if (mScaleType == stLogarithmic)
4288 mRange = mRange.sanitizedForLogScale();
4291 mRange = mRange.sanitizedForLinScale();
4293 mCachedMarginValid = false;
4294 emit rangeChanged(mRange);
4295 emit rangeChanged(mRange, oldRange);
4301 Sets the range of the axis.
4303 The \a position coordinate indicates together with the \a alignment parameter, where the new
4304 range will be positioned. \a size defines the size of the new axis range. \a alignment may be
4305 Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
4306 or center of the range to be aligned with \a position. Any other values of \a alignment will
4307 default to Qt::AlignCenter.
4309 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4311 if (alignment == Qt::AlignLeft)
4312 setRange(position, position+size);
4313 else if (alignment == Qt::AlignRight)
4314 setRange(position-size, position);
4315 else // alignment == Qt::AlignCenter
4316 setRange(position-size/2.0, position+size/2.0);
4320 Sets the lower bound of the axis range. The upper bound is not changed.
4323 void QCPAxis::setRangeLower(double lower)
4325 if (mRange.lower == lower)
4328 QCPRange oldRange = mRange;
4329 mRange.lower = lower;
4330 if (mScaleType == stLogarithmic)
4332 mRange = mRange.sanitizedForLogScale();
4335 mRange = mRange.sanitizedForLinScale();
4337 mCachedMarginValid = false;
4338 emit rangeChanged(mRange);
4339 emit rangeChanged(mRange, oldRange);
4343 Sets the upper bound of the axis range. The lower bound is not changed.
4346 void QCPAxis::setRangeUpper(double upper)
4348 if (mRange.upper == upper)
4351 QCPRange oldRange = mRange;
4352 mRange.upper = upper;
4353 if (mScaleType == stLogarithmic)
4355 mRange = mRange.sanitizedForLogScale();
4358 mRange = mRange.sanitizedForLinScale();
4360 mCachedMarginValid = false;
4361 emit rangeChanged(mRange);
4362 emit rangeChanged(mRange, oldRange);
4366 Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
4367 axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
4368 direction of increasing values is inverted.
4370 Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
4371 of the \ref setRange interface will still reference the mathematically smaller number than the \a
4374 void QCPAxis::setRangeReversed(bool reversed)
4376 if (mRangeReversed != reversed)
4378 mRangeReversed = reversed;
4379 mCachedMarginValid = false;
4384 Sets whether the tick positions should be calculated automatically (either from an automatically
4385 generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
4387 If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
4388 For these manual ticks you may let QCPAxis generate the appropriate labels automatically by
4389 leaving \ref setAutoTickLabels set to true. If you also wish to control the displayed labels
4390 manually, set \ref setAutoTickLabels to false and provide the label strings with \ref
4391 setTickVectorLabels.
4393 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4394 vectors in a slot connected to the \ref ticksRequest signal.
4396 \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep
4398 void QCPAxis::setAutoTicks(bool on)
4400 if (mAutoTicks != on)
4403 mCachedMarginValid = false;
4408 When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be
4409 generated in the visible range, approximately.
4411 It's not guaranteed that this number of ticks is met exactly, but approximately within a
4412 tolerance of about two.
4414 Only values greater than zero are accepted as \a approximateCount.
4416 \see setAutoTickStep, setAutoTicks, setAutoSubTicks
4418 void QCPAxis::setAutoTickCount(int approximateCount)
4420 if (mAutoTickCount != approximateCount)
4422 if (approximateCount > 0)
4424 mAutoTickCount = approximateCount;
4425 mCachedMarginValid = false;
4427 qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4432 Sets whether the tick labels are generated automatically. Depending on the tick label type (\ref
4433 ltNumber or \ref ltDateTime), the labels will either show the coordinate as floating point
4434 number (\ref setNumberFormat), or a date/time formatted according to \ref setDateTimeFormat.
4436 If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
4437 is usually used in a combination with \ref setAutoTicks set to false for complete control over
4438 tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
4439 etc. as tick labels.
4441 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4442 vectors in a slot connected to the \ref ticksRequest signal.
4446 void QCPAxis::setAutoTickLabels(bool on)
4448 if (mAutoTickLabels != on)
4450 mAutoTickLabels = on;
4451 mCachedMarginValid = false;
4456 Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
4457 automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
4460 The number of ticks the algorithm aims for within the visible range can be specified with \ref
4463 If \a on is set to false, you may set the tick step manually with \ref setTickStep.
4465 \see setAutoTicks, setAutoSubTicks, setAutoTickCount
4467 void QCPAxis::setAutoTickStep(bool on)
4469 if (mAutoTickStep != on)
4472 mCachedMarginValid = false;
4477 Sets whether the number of sub ticks in one tick interval is determined automatically. This
4478 works, as long as the tick step mantissa is a multiple of 0.5. When \ref setAutoTickStep is
4479 enabled, this is always the case.
4481 When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
4483 \see setAutoTickCount, setAutoTicks, setAutoTickStep
4485 void QCPAxis::setAutoSubTicks(bool on)
4487 if (mAutoSubTicks != on)
4490 mCachedMarginValid = false;
4495 Sets whether tick marks are displayed.
4497 Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
4498 that, see \ref setTickLabels.
4500 void QCPAxis::setTicks(bool show)
4505 mCachedMarginValid = false;
4510 Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
4512 void QCPAxis::setTickLabels(bool show)
4514 if (mTickLabels != show)
4517 mCachedMarginValid = false;
4522 Sets the distance between the axis base line (including any outward ticks) and the tick labels.
4523 \see setLabelPadding, setPadding
4525 void QCPAxis::setTickLabelPadding(int padding)
4527 if (mAxisPainter->tickLabelPadding != padding)
4529 mAxisPainter->tickLabelPadding = padding;
4530 mCachedMarginValid = false;
4535 Sets whether the tick labels display numbers or dates/times.
4537 If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.
4539 If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.
4541 In QCustomPlot, date/time coordinates are <tt>double</tt> numbers representing the seconds since
4542 1970-01-01T00:00:00 UTC. This format can be retrieved from QDateTime objects with the
4543 QDateTime::toTime_t() function. Since this only gives a resolution of one second, there is also
4544 the QDateTime::toMSecsSinceEpoch() function which returns the timespan described above in
4545 milliseconds. Divide its return value by 1000.0 to get a value with the format needed for
4546 date/time plotting, with a resolution of one millisecond.
4548 Using the toMSecsSinceEpoch function allows dates that go back to 2nd January 4713 B.C.
4549 (represented by a negative number), unlike the toTime_t function, which works with unsigned
4550 integers and thus only goes back to 1st January 1970. So both for range and accuracy, use of
4551 toMSecsSinceEpoch()/1000.0 should be preferred as key coordinate for date/time axes.
4555 void QCPAxis::setTickLabelType(LabelType type)
4557 if (mTickLabelType != type)
4559 mTickLabelType = type;
4560 mCachedMarginValid = false;
4565 Sets the font of the tick labels.
4567 \see setTickLabels, setTickLabelColor
4569 void QCPAxis::setTickLabelFont(const QFont &font)
4571 if (font != mTickLabelFont)
4573 mTickLabelFont = font;
4574 mCachedMarginValid = false;
4579 Sets the color of the tick labels.
4581 \see setTickLabels, setTickLabelFont
4583 void QCPAxis::setTickLabelColor(const QColor &color)
4585 if (color != mTickLabelColor)
4587 mTickLabelColor = color;
4588 mCachedMarginValid = false;
4593 Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
4594 the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
4595 from -90 to 90 degrees.
4597 If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
4598 other angles, the label is drawn with an offset such that it seems to point toward or away from
4601 void QCPAxis::setTickLabelRotation(double degrees)
4603 if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4605 mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4606 mCachedMarginValid = false;
4611 Sets whether the tick labels (numbers) shall appear inside or outside the axis rect.
4613 The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels
4614 to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels
4615 appear on the inside are additionally clipped to the axis rect.
4617 void QCPAxis::setTickLabelSide(LabelSide side)
4619 mAxisPainter->tickLabelSide = side;
4620 mCachedMarginValid = false;
4624 Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
4625 for details about the \a format string, see the documentation of QDateTime::toString().
4627 Newlines can be inserted with "\n".
4629 \see setDateTimeSpec
4631 void QCPAxis::setDateTimeFormat(const QString &format)
4633 if (mDateTimeFormat != format)
4635 mDateTimeFormat = format;
4636 mCachedMarginValid = false;
4641 Sets the time spec that is used for the date time values when \ref setTickLabelType is \ref
4644 The default value of QDateTime objects (and also QCustomPlot) is <tt>Qt::LocalTime</tt>. However,
4645 if the date time values passed to QCustomPlot are given in the UTC spec, set \a
4646 timeSpec to <tt>Qt::UTC</tt> to get the correct axis labels.
4648 \see setDateTimeFormat
4650 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4652 mDateTimeSpec = timeSpec;
4656 Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
4657 ltNumber). This \a formatCode is an extended version of the format code used e.g. by
4658 QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
4659 section in the detailed description of the QString class. \a formatCode is a string of one, two
4660 or three characters. The first character is identical to the normal format code used by Qt. In
4661 short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
4662 whichever is shorter.
4664 The second and third characters are optional and specific to QCustomPlot:\n
4665 If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
4666 "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
4667 "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
4668 [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
4669 If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
4670 be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
4671 cross and 183 (0xB7) for the dot.
4673 If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
4674 option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4675 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4676 part). To only display the decimal power, set the number precision to zero with \ref
4679 Examples for \a formatCode:
4680 \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
4681 normal scientific format is used
4682 \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
4683 beautifully typeset decimal powers and a dot as multiplication sign
4684 \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
4686 \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
4687 powers. Format code will be reduced to 'f'.
4688 \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
4689 code will not be changed.
4691 void QCPAxis::setNumberFormat(const QString &formatCode)
4693 if (formatCode.isEmpty())
4695 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4698 mCachedMarginValid = false;
4700 // interpret first char as number format char:
4701 QString allowedFormatChars(QLatin1String("eEfgG"));
4702 if (allowedFormatChars.contains(formatCode.at(0)))
4704 mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4707 qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4710 if (formatCode.length() < 2)
4712 mNumberBeautifulPowers = false;
4713 mAxisPainter->numberMultiplyCross = false;
4717 // interpret second char as indicator for beautiful decimal powers:
4718 if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4720 mNumberBeautifulPowers = true;
4723 qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4726 if (formatCode.length() < 3)
4728 mAxisPainter->numberMultiplyCross = false;
4732 // interpret third char as indicator for dot or cross multiplication symbol:
4733 if (formatCode.at(2) == QLatin1Char('c'))
4735 mAxisPainter->numberMultiplyCross = true;
4736 } else if (formatCode.at(2) == QLatin1Char('d'))
4738 mAxisPainter->numberMultiplyCross = false;
4741 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4747 Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
4748 for details. The effect of precisions are most notably for number Formats starting with 'e', see
4749 \ref setNumberFormat
4751 If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
4752 setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
4753 usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
4754 scaling (the redundant "1 [multiplication sign]" part). To only display the decimal power "10
4755 [superscript] n", set \a precision to zero.
4757 void QCPAxis::setNumberPrecision(int precision)
4759 if (mNumberPrecision != precision)
4761 mNumberPrecision = precision;
4762 mCachedMarginValid = false;
4767 If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
4768 The tick step is the interval between (major) ticks, in plot coordinates.
4769 \see setSubTickCount
4771 void QCPAxis::setTickStep(double step)
4773 if (mTickStep != step)
4776 mCachedMarginValid = false;
4781 If you want full control over what ticks (and possibly labels) the axes show, this function is
4782 used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
4783 the provided tick vector will be overwritten with automatically generated tick coordinates upon
4784 replot. The labels of the ticks can be generated automatically when \ref setAutoTickLabels is
4785 left enabled. If it is disabled, you can set the labels manually with \ref setTickVectorLabels.
4787 \a vec is a vector containing the positions of the ticks, in plot coordinates.
4789 \warning \a vec must be sorted in ascending order, no additional checks are made to ensure this.
4791 \see setTickVectorLabels
4793 void QCPAxis::setTickVector(const QVector<double> &vec)
4795 // don't check whether mTickVector != vec here, because it takes longer than we would save
4797 mCachedMarginValid = false;
4801 If you want full control over what ticks and labels the axes show, this function is used to set a
4802 number of QStrings that will be displayed at the tick positions which you need to provide with
4803 \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
4804 \ref setAutoTicks and \ref setAutoTickLabels first.)
4806 \a vec is a vector containing the labels of the ticks. The entries correspond to the respective
4807 indices in the tick vector, passed via \ref setTickVector.
4811 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4813 // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4814 mTickVectorLabels = vec;
4815 mCachedMarginValid = false;
4819 Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
4820 plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
4821 zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4822 they won't collide with the ticks.
4824 \see setSubTickLength, setTickLengthIn, setTickLengthOut
4826 void QCPAxis::setTickLength(int inside, int outside)
4828 setTickLengthIn(inside);
4829 setTickLengthOut(outside);
4833 Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4836 \see setTickLengthOut, setTickLength, setSubTickLength
4838 void QCPAxis::setTickLengthIn(int inside)
4840 if (mAxisPainter->tickLengthIn != inside)
4842 mAxisPainter->tickLengthIn = inside;
4847 Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
4848 outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4849 increase their distance to the axis accordingly, so they won't collide with the ticks.
4851 \see setTickLengthIn, setTickLength, setSubTickLength
4853 void QCPAxis::setTickLengthOut(int outside)
4855 if (mAxisPainter->tickLengthOut != outside)
4857 mAxisPainter->tickLengthOut = outside;
4858 mCachedMarginValid = false; // only outside tick length can change margin
4863 Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
4864 divides the tick intervals in four sub intervals.
4866 By default, the number of sub ticks is chosen automatically in a reasonable manner as long as the
4867 mantissa of the tick step is a multiple of 0.5. When \ref setAutoTickStep is enabled, this is
4870 If you want to disable automatic sub tick count and use this function to set the count manually,
4871 see \ref setAutoSubTicks.
4873 void QCPAxis::setSubTickCount(int count)
4875 mSubTickCount = count;
4879 Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
4880 the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4881 than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4882 so they won't collide with the ticks.
4884 \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4886 void QCPAxis::setSubTickLength(int inside, int outside)
4888 setSubTickLengthIn(inside);
4889 setSubTickLengthOut(outside);
4893 Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4896 \see setSubTickLengthOut, setSubTickLength, setTickLength
4898 void QCPAxis::setSubTickLengthIn(int inside)
4900 if (mAxisPainter->subTickLengthIn != inside)
4902 mAxisPainter->subTickLengthIn = inside;
4907 Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
4908 outside the plot. If \a outside is greater than zero, the tick labels will increase their
4909 distance to the axis accordingly, so they won't collide with the ticks.
4911 \see setSubTickLengthIn, setSubTickLength, setTickLength
4913 void QCPAxis::setSubTickLengthOut(int outside)
4915 if (mAxisPainter->subTickLengthOut != outside)
4917 mAxisPainter->subTickLengthOut = outside;
4918 mCachedMarginValid = false; // only outside tick length can change margin
4923 Sets the pen, the axis base line is drawn with.
4925 \see setTickPen, setSubTickPen
4927 void QCPAxis::setBasePen(const QPen &pen)
4933 Sets the pen, tick marks will be drawn with.
4935 \see setTickLength, setBasePen
4937 void QCPAxis::setTickPen(const QPen &pen)
4943 Sets the pen, subtick marks will be drawn with.
4945 \see setSubTickCount, setSubTickLength, setBasePen
4947 void QCPAxis::setSubTickPen(const QPen &pen)
4953 Sets the font of the axis label.
4957 void QCPAxis::setLabelFont(const QFont &font)
4959 if (mLabelFont != font)
4962 mCachedMarginValid = false;
4967 Sets the color of the axis label.
4971 void QCPAxis::setLabelColor(const QColor &color)
4973 mLabelColor = color;
4977 Sets the text of the axis label that will be shown below/above or next to the axis, depending on
4978 its orientation. To disable axis labels, pass an empty string as \a str.
4980 void QCPAxis::setLabel(const QString &str)
4985 mCachedMarginValid = false;
4990 Sets the distance between the tick labels and the axis label.
4992 \see setTickLabelPadding, setPadding
4994 void QCPAxis::setLabelPadding(int padding)
4996 if (mAxisPainter->labelPadding != padding)
4998 mAxisPainter->labelPadding = padding;
4999 mCachedMarginValid = false;
5004 Sets the padding of the axis.
5006 When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space,
5009 The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled.
5011 \see setLabelPadding, setTickLabelPadding
5013 void QCPAxis::setPadding(int padding)
5015 if (mPadding != padding)
5018 mCachedMarginValid = false;
5023 Sets the offset the axis has to its axis rect side.
5025 If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
5026 only the offset of the inner most axis has meaning (even if it is set to be invisible). The
5027 offset of the other, outer axes is controlled automatically, to place them at appropriate
5030 void QCPAxis::setOffset(int offset)
5032 mAxisPainter->offset = offset;
5036 Sets the font that is used for tick labels when they are selected.
5038 \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5040 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5042 if (font != mSelectedTickLabelFont)
5044 mSelectedTickLabelFont = font;
5045 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5050 Sets the font that is used for the axis label when it is selected.
5052 \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5054 void QCPAxis::setSelectedLabelFont(const QFont &font)
5056 mSelectedLabelFont = font;
5057 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5061 Sets the color that is used for tick labels when they are selected.
5063 \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5065 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5067 if (color != mSelectedTickLabelColor)
5069 mSelectedTickLabelColor = color;
5074 Sets the color that is used for the axis label when it is selected.
5076 \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5078 void QCPAxis::setSelectedLabelColor(const QColor &color)
5080 mSelectedLabelColor = color;
5084 Sets the pen that is used to draw the axis base line when selected.
5086 \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5088 void QCPAxis::setSelectedBasePen(const QPen &pen)
5090 mSelectedBasePen = pen;
5094 Sets the pen that is used to draw the (major) ticks when selected.
5096 \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5098 void QCPAxis::setSelectedTickPen(const QPen &pen)
5100 mSelectedTickPen = pen;
5104 Sets the pen that is used to draw the subticks when selected.
5106 \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5108 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5110 mSelectedSubTickPen = pen;
5114 Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available
5117 For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending.
5118 Note that this meaning does not change when the axis range is reversed with \ref
5123 void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
5125 mAxisPainter->lowerEnding = ending;
5129 Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available
5132 For horizontal axes, this method refers to the right ending, for vertical axes the top ending.
5133 Note that this meaning does not change when the axis range is reversed with \ref
5138 void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
5140 mAxisPainter->upperEnding = ending;
5144 If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
5145 bounds of the range. The range is simply moved by \a diff.
5147 If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
5148 corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
5150 void QCPAxis::moveRange(double diff)
5152 QCPRange oldRange = mRange;
5153 if (mScaleType == stLinear)
5155 mRange.lower += diff;
5156 mRange.upper += diff;
5157 } else // mScaleType == stLogarithmic
5159 mRange.lower *= diff;
5160 mRange.upper *= diff;
5162 mCachedMarginValid = false;
5163 emit rangeChanged(mRange);
5164 emit rangeChanged(mRange, oldRange);
5168 Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
5169 factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
5170 coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
5171 around 1.0 will have moved symmetrically closer to 1.0).
5173 void QCPAxis::scaleRange(double factor, double center)
5175 QCPRange oldRange = mRange;
5176 if (mScaleType == stLinear)
5179 newRange.lower = (mRange.lower-center)*factor + center;
5180 newRange.upper = (mRange.upper-center)*factor + center;
5181 if (QCPRange::validRange(newRange))
5182 mRange = newRange.sanitizedForLinScale();
5183 } else // mScaleType == stLogarithmic
5185 if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5188 newRange.lower = qPow(mRange.lower/center, factor)*center;
5189 newRange.upper = qPow(mRange.upper/center, factor)*center;
5190 if (QCPRange::validRange(newRange))
5191 mRange = newRange.sanitizedForLogScale();
5193 qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5195 mCachedMarginValid = false;
5196 emit rangeChanged(mRange);
5197 emit rangeChanged(mRange, oldRange);
5201 Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will
5202 be done around the center of the current axis range.
5204 For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs
5205 plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the
5208 This is an operation that changes the range of this axis once, it doesn't fix the scale ratio
5209 indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent
5210 won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent
5213 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5215 int otherPixelSize, ownPixelSize;
5217 if (otherAxis->orientation() == Qt::Horizontal)
5218 otherPixelSize = otherAxis->axisRect()->width();
5220 otherPixelSize = otherAxis->axisRect()->height();
5222 if (orientation() == Qt::Horizontal)
5223 ownPixelSize = axisRect()->width();
5225 ownPixelSize = axisRect()->height();
5227 double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5228 setRange(range().center(), newRangeSize, Qt::AlignCenter);
5232 Changes the axis range such that all plottables associated with this axis are fully visible in
5235 \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
5237 void QCPAxis::rescale(bool onlyVisiblePlottables)
5239 QList<QCPAbstractPlottable*> p = plottables();
5241 bool haveRange = false;
5242 for (int i=0; i<p.size(); ++i)
5244 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5246 QCPRange plottableRange;
5247 bool currentFoundRange;
5248 QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5249 if (mScaleType == stLogarithmic)
5250 signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5251 if (p.at(i)->keyAxis() == this)
5252 plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5254 plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5255 if (currentFoundRange)
5258 newRange = plottableRange;
5260 newRange.expand(plottableRange);
5266 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
5268 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
5269 if (mScaleType == stLinear)
5271 newRange.lower = center-mRange.size()/2.0;
5272 newRange.upper = center+mRange.size()/2.0;
5273 } else // mScaleType == stLogarithmic
5275 newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5276 newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5284 Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
5286 double QCPAxis::pixelToCoord(double value) const
5288 if (orientation() == Qt::Horizontal)
5290 if (mScaleType == stLinear)
5292 if (!mRangeReversed)
5293 return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5295 return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5296 } else // mScaleType == stLogarithmic
5298 if (!mRangeReversed)
5299 return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5301 return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5303 } else // orientation() == Qt::Vertical
5305 if (mScaleType == stLinear)
5307 if (!mRangeReversed)
5308 return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5310 return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5311 } else // mScaleType == stLogarithmic
5313 if (!mRangeReversed)
5314 return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5316 return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5322 Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
5324 double QCPAxis::coordToPixel(double value) const
5326 if (orientation() == Qt::Horizontal)
5328 if (mScaleType == stLinear)
5330 if (!mRangeReversed)
5331 return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5333 return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5334 } else // mScaleType == stLogarithmic
5336 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5337 return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5338 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5339 return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5342 if (!mRangeReversed)
5343 return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5345 return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5348 } else // orientation() == Qt::Vertical
5350 if (mScaleType == stLinear)
5352 if (!mRangeReversed)
5353 return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5355 return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5356 } else // mScaleType == stLogarithmic
5358 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5359 return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5360 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5361 return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5364 if (!mRangeReversed)
5365 return mAxisRect->bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5367 return mAxisRect->bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5374 Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
5375 is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
5376 function does not change the current selection state of the axis.
5378 If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
5380 \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
5382 QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const
5387 if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5389 else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5390 return spTickLabels;
5391 else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5397 /* inherits documentation from base class */
5398 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5400 if (!mParentPlot) return -1;
5401 SelectablePart part = getPartAt(pos);
5402 if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5406 details->setValue(part);
5407 return mParentPlot->selectionTolerance()*0.99;
5411 Returns a list of all the plottables that have this axis as key or value axis.
5413 If you are only interested in plottables of type QCPGraph, see \ref graphs.
5417 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5419 QList<QCPAbstractPlottable*> result;
5420 if (!mParentPlot) return result;
5422 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5424 if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5425 result.append(mParentPlot->mPlottables.at(i));
5431 Returns a list of all the graphs that have this axis as key or value axis.
5433 \see plottables, items
5435 QList<QCPGraph*> QCPAxis::graphs() const
5437 QList<QCPGraph*> result;
5438 if (!mParentPlot) return result;
5440 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5442 if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5443 result.append(mParentPlot->mGraphs.at(i));
5449 Returns a list of all the items that are associated with this axis. An item is considered
5450 associated with an axis if at least one of its positions uses the axis as key or value axis.
5452 \see plottables, graphs
5454 QList<QCPAbstractItem*> QCPAxis::items() const
5456 QList<QCPAbstractItem*> result;
5457 if (!mParentPlot) return result;
5459 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5461 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5462 for (int posId=0; posId<positions.size(); ++posId)
5464 if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5466 result.append(mParentPlot->mItems.at(itemId));
5475 Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to
5476 QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)
5478 QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
5482 case QCP::msLeft: return atLeft;
5483 case QCP::msRight: return atRight;
5484 case QCP::msTop: return atTop;
5485 case QCP::msBottom: return atBottom;
5488 qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5493 Returns the axis type that describes the opposite axis of an axis with the specified \a type.
5495 QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
5499 case atLeft: return atRight; break;
5500 case atRight: return atLeft; break;
5501 case atBottom: return atTop; break;
5502 case atTop: return atBottom; break;
5503 default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5509 This function is called to prepare the tick vector, sub tick vector and tick label vector. If
5510 \ref setAutoTicks is set to true, appropriate tick values are determined automatically via \ref
5511 generateAutoTicks. If it's set to false, the signal ticksRequest is emitted, which can be used to
5512 provide external tick positions. Then the sub tick vectors and tick label vectors are created.
5514 void QCPAxis::setupTickVectors()
5516 if (!mParentPlot) return;
5517 if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5519 // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5522 generateAutoTicks();
5525 emit ticksRequest();
5528 visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
5529 if (mTickVector.isEmpty())
5531 mSubTickVector.clear();
5535 // generate subticks between ticks:
5536 mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5537 if (mSubTickCount > 0)
5539 double subTickStep = 0;
5540 double subTickPosition = 0;
5541 int subTickIndex = 0;
5543 int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick-1 : mLowestVisibleTick;
5544 int highTick = mHighestVisibleTick < mTickVector.size()-1 ? mHighestVisibleTick+1 : mHighestVisibleTick;
5545 for (int i=lowTick+1; i<=highTick; ++i)
5547 subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5548 for (int k=1; k<=mSubTickCount; ++k)
5550 subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5551 if (subTickPosition < mRange.lower)
5553 if (subTickPosition > mRange.upper)
5558 mSubTickVector[subTickIndex] = subTickPosition;
5563 mSubTickVector.resize(subTickIndex);
5566 // generate tick labels according to tick positions:
5567 if (mAutoTickLabels)
5569 int vecsize = mTickVector.size();
5570 mTickVectorLabels.resize(vecsize);
5571 if (mTickLabelType == ltNumber)
5573 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5574 mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5575 } else if (mTickLabelType == ltDateTime)
5577 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5579 #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")
5580 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5582 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5586 } else // mAutoTickLabels == false
5588 if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5590 emit ticksRequest();
5592 // make sure provided tick label vector has correct (minimal) length:
5593 if (mTickVectorLabels.size() < mTickVector.size())
5594 mTickVectorLabels.resize(mTickVector.size());
5600 If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
5601 generate reasonable tick positions (and subtick count). The algorithm tries to create
5602 approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount).
5604 If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is generated at every
5605 power of the current logarithm base, set via \ref setScaleLogBase.
5607 void QCPAxis::generateAutoTicks()
5609 if (mScaleType == stLinear)
5613 // Generate tick positions according to linear scaling:
5614 mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5615 double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5616 double tickStepMantissa = mTickStep/magnitudeFactor;
5617 if (tickStepMantissa < 5)
5619 // round digit after decimal point to 0.5
5620 mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5623 // round to first digit in multiples of 2
5624 mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5628 mSubTickCount = calculateAutoSubTickCount(mTickStep);
5629 // Generate tick positions according to mTickStep:
5630 qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5631 qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5632 int tickcount = lastStep-firstStep+1;
5633 if (tickcount < 0) tickcount = 0;
5634 mTickVector.resize(tickcount);
5635 for (int i=0; i<tickcount; ++i)
5636 mTickVector[i] = (firstStep+i)*mTickStep;
5637 } else // mScaleType == stLogarithmic
5639 // Generate tick positions according to logbase scaling:
5640 if (mRange.lower > 0 && mRange.upper > 0) // positive range
5642 double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5643 double currentMag = lowerMag;
5644 mTickVector.clear();
5645 mTickVector.append(currentMag);
5646 while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5648 currentMag *= mScaleLogBase;
5649 mTickVector.append(currentMag);
5651 } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5653 double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5654 double currentMag = lowerMag;
5655 mTickVector.clear();
5656 mTickVector.append(currentMag);
5657 while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5659 currentMag /= mScaleLogBase;
5660 mTickVector.append(currentMag);
5662 } else // invalid range for logarithmic scale, because lower and upper have different sign
5664 mTickVector.clear();
5665 qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5672 Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
5673 tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
5674 Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
5675 because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667. Note
5676 that a subtick count of 4 means dividing the major tick step into 5 sections.
5678 This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
5679 with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
5680 fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
5683 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5685 int result = mSubTickCount; // default to current setting, if no proper value can be found
5687 // get mantissa of tickstep:
5688 double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5689 double tickStepMantissa = tickStep/magnitudeFactor;
5691 // separate integer and fractional part of mantissa:
5692 double epsilon = 0.01;
5695 double fracPart = modf(tickStepMantissa, &intPartf);
5698 // handle cases with (almost) integer mantissa:
5699 if (fracPart < epsilon || 1.0-fracPart < epsilon)
5701 if (1.0-fracPart < epsilon)
5705 case 1: result = 4; break; // 1.0 -> 0.2 substep
5706 case 2: result = 3; break; // 2.0 -> 0.5 substep
5707 case 3: result = 2; break; // 3.0 -> 1.0 substep
5708 case 4: result = 3; break; // 4.0 -> 1.0 substep
5709 case 5: result = 4; break; // 5.0 -> 1.0 substep
5710 case 6: result = 2; break; // 6.0 -> 2.0 substep
5711 case 7: result = 6; break; // 7.0 -> 1.0 substep
5712 case 8: result = 3; break; // 8.0 -> 2.0 substep
5713 case 9: result = 2; break; // 9.0 -> 3.0 substep
5717 // handle cases with significantly fractional mantissa:
5718 if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5722 case 1: result = 2; break; // 1.5 -> 0.5 substep
5723 case 2: result = 4; break; // 2.5 -> 0.5 substep
5724 case 3: result = 4; break; // 3.5 -> 0.7 substep
5725 case 4: result = 2; break; // 4.5 -> 1.5 substep
5726 case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5727 case 6: result = 4; break; // 6.5 -> 1.3 substep
5728 case 7: result = 2; break; // 7.5 -> 2.5 substep
5729 case 8: result = 4; break; // 8.5 -> 1.7 substep
5730 case 9: result = 4; break; // 9.5 -> 1.9 substep
5733 // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5739 /* inherits documentation from base class */
5740 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5743 SelectablePart part = details.value<SelectablePart>();
5744 if (mSelectableParts.testFlag(part))
5746 SelectableParts selBefore = mSelectedParts;
5747 setSelectedParts(additive ? mSelectedParts^part : part);
5748 if (selectionStateChanged)
5749 *selectionStateChanged = mSelectedParts != selBefore;
5753 /* inherits documentation from base class */
5754 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5756 SelectableParts selBefore = mSelectedParts;
5757 setSelectedParts(mSelectedParts & ~mSelectableParts);
5758 if (selectionStateChanged)
5759 *selectionStateChanged = mSelectedParts != selBefore;
5764 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
5765 before drawing axis lines.
5767 This is the antialiasing state the painter passed to the \ref draw method is in by default.
5769 This function takes into account the local setting of the antialiasing flag as well as the
5770 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
5771 QCustomPlot::setNotAntialiasedElements.
5775 void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
5777 applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
5782 Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
5785 void QCPAxis::draw(QCPPainter *painter)
5787 const int lowTick = mLowestVisibleTick;
5788 const int highTick = mHighestVisibleTick;
5789 QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5790 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5791 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5792 tickPositions.reserve(highTick-lowTick+1);
5793 tickLabels.reserve(highTick-lowTick+1);
5794 subTickPositions.reserve(mSubTickVector.size());
5798 for (int i=lowTick; i<=highTick; ++i)
5800 tickPositions.append(coordToPixel(mTickVector.at(i)));
5802 tickLabels.append(mTickVectorLabels.at(i));
5805 if (mSubTickCount > 0)
5807 const int subTickCount = mSubTickVector.size();
5808 for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5809 subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5812 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5813 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5814 mAxisPainter->type = mAxisType;
5815 mAxisPainter->basePen = getBasePen();
5816 mAxisPainter->labelFont = getLabelFont();
5817 mAxisPainter->labelColor = getLabelColor();
5818 mAxisPainter->label = mLabel;
5819 mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber;
5820 mAxisPainter->tickPen = getTickPen();
5821 mAxisPainter->subTickPen = getSubTickPen();
5822 mAxisPainter->tickLabelFont = getTickLabelFont();
5823 mAxisPainter->tickLabelColor = getTickLabelColor();
5824 mAxisPainter->axisRect = mAxisRect->rect();
5825 mAxisPainter->viewportRect = mParentPlot->viewport();
5826 mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5827 mAxisPainter->reversedEndings = mRangeReversed;
5828 mAxisPainter->tickPositions = tickPositions;
5829 mAxisPainter->tickLabels = tickLabels;
5830 mAxisPainter->subTickPositions = subTickPositions;
5831 mAxisPainter->draw(painter);
5836 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
5837 the current range. The return values are indices of the tick vector, not the positions of the
5840 The actual use of this function is when an external tick vector is provided, since it might
5841 exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
5844 If all ticks are outside the axis range, an inverted range is returned, i.e. highIndex will be
5845 smaller than lowIndex. There is one case, where this function returns indices that are not really
5846 visible in the current axis range: When the tick spacing is larger than the axis range size and
5847 one tick is below the axis range and the next tick is already above the axis range. Because in
5848 such cases it is usually desirable to know the tick pair, to draw proper subticks.
5850 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5852 bool lowFound = false;
5853 bool highFound = false;
5857 for (int i=0; i < mTickVector.size(); ++i)
5859 if (mTickVector.at(i) >= mRange.lower)
5866 for (int i=mTickVector.size()-1; i >= 0; --i)
5868 if (mTickVector.at(i) <= mRange.upper)
5876 if (!lowFound && highFound)
5877 lowIndex = highIndex+1;
5878 else if (lowFound && !highFound)
5879 highIndex = lowIndex-1;
5884 A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
5885 scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
5886 This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
5888 \see basePow, setScaleLogBase, setScaleType
5890 double QCPAxis::baseLog(double value) const
5892 return qLn(value)*mScaleLogBaseLogInv;
5897 A power function with the base mScaleLogBase, used mostly for coordinate transforms in
5898 logarithmic scales with arbitrary log base.
5900 \see baseLog, setScaleLogBase, setScaleType
5902 double QCPAxis::basePow(double value) const
5904 return qPow(mScaleLogBase, value);
5909 Returns the pen that is used to draw the axis base line. Depending on the selection state, this
5910 is either mSelectedBasePen or mBasePen.
5912 QPen QCPAxis::getBasePen() const
5914 return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5919 Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
5920 is either mSelectedTickPen or mTickPen.
5922 QPen QCPAxis::getTickPen() const
5924 return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5929 Returns the pen that is used to draw the subticks. Depending on the selection state, this
5930 is either mSelectedSubTickPen or mSubTickPen.
5932 QPen QCPAxis::getSubTickPen() const
5934 return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
5939 Returns the font that is used to draw the tick labels. Depending on the selection state, this
5940 is either mSelectedTickLabelFont or mTickLabelFont.
5942 QFont QCPAxis::getTickLabelFont() const
5944 return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
5949 Returns the font that is used to draw the axis label. Depending on the selection state, this
5950 is either mSelectedLabelFont or mLabelFont.
5952 QFont QCPAxis::getLabelFont() const
5954 return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
5959 Returns the color that is used to draw the tick labels. Depending on the selection state, this
5960 is either mSelectedTickLabelColor or mTickLabelColor.
5962 QColor QCPAxis::getTickLabelColor() const
5964 return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
5969 Returns the color that is used to draw the axis label. Depending on the selection state, this
5970 is either mSelectedLabelColor or mLabelColor.
5972 QColor QCPAxis::getLabelColor() const
5974 return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
5979 Returns the appropriate outward margin for this axis. It is needed if \ref
5980 QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref
5981 atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom
5982 margin and so forth. For the calculation, this function goes through similar steps as \ref draw,
5983 so changing one function likely requires the modification of the other one as well.
5985 The margin consists of the outward tick length, tick label padding, tick label size, label
5986 padding, label size, and padding.
5988 The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc.
5989 unchanged are very fast.
5991 int QCPAxis::calculateMargin()
5993 if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
5996 if (mCachedMarginValid)
5997 return mCachedMargin;
5999 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6002 int lowTick, highTick;
6003 visibleTickBounds(lowTick, highTick);
6004 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6005 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6006 tickPositions.reserve(highTick-lowTick+1);
6007 tickLabels.reserve(highTick-lowTick+1);
6010 for (int i=lowTick; i<=highTick; ++i)
6012 tickPositions.append(coordToPixel(mTickVector.at(i)));
6014 tickLabels.append(mTickVectorLabels.at(i));
6017 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6018 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6019 mAxisPainter->type = mAxisType;
6020 mAxisPainter->labelFont = getLabelFont();
6021 mAxisPainter->label = mLabel;
6022 mAxisPainter->tickLabelFont = mTickLabelFont;
6023 mAxisPainter->axisRect = mAxisRect->rect();
6024 mAxisPainter->viewportRect = mParentPlot->viewport();
6025 mAxisPainter->tickPositions = tickPositions;
6026 mAxisPainter->tickLabels = tickLabels;
6027 margin += mAxisPainter->size();
6030 mCachedMargin = margin;
6031 mCachedMarginValid = true;
6035 /* inherits documentation from base class */
6036 QCP::Interaction QCPAxis::selectionCategory() const
6038 return QCP::iSelectAxes;
6042 ////////////////////////////////////////////////////////////////////////////////////////////////////
6043 //////////////////// QCPAxisPainterPrivate
6044 ////////////////////////////////////////////////////////////////////////////////////////////////////
6046 /*! \class QCPAxisPainterPrivate
6051 This is a private class and not part of the public QCustomPlot interface.
6053 It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
6054 axis label. It also buffers the labels to reduce replot times. The parameters are configured by
6055 directly accessing the public member variables.
6059 Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
6060 redraw, to utilize the caching mechanisms.
6062 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
6063 type(QCPAxis::atLeft),
6064 basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6065 lowerEnding(QCPLineEnding::esNone),
6066 upperEnding(QCPLineEnding::esNone),
6068 tickLabelPadding(0),
6069 tickLabelRotation(0),
6070 tickLabelSide(QCPAxis::lsOutside),
6071 substituteExponent(true),
6072 numberMultiplyCross(false),
6076 subTickLengthOut(0),
6077 tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6078 subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6080 abbreviateDecimalPowers(false),
6081 reversedEndings(false),
6082 mParentPlot(parentPlot),
6083 mLabelCache(16) // cache at most 16 (tick) labels
6087 QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
6093 Draws the axis with the specified \a painter.
6095 The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
6098 void QCPAxisPainterPrivate::draw(QCPPainter *painter)
6100 QByteArray newHash = generateLabelParameterHash();
6101 if (newHash != mLabelParameterHash)
6103 mLabelCache.clear();
6104 mLabelParameterHash = newHash;
6110 case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6111 case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6112 case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
6113 case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6116 double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6119 case QCPAxis::atTop: yCor = -1; break;
6120 case QCPAxis::atRight: xCor = 1; break;
6126 painter->setPen(basePen);
6127 if (QCPAxis::orientation(type) == Qt::Horizontal)
6128 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6130 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6131 if (reversedEndings)
6132 baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6133 painter->drawLine(baseLine);
6136 if (!tickPositions.isEmpty())
6138 painter->setPen(tickPen);
6139 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6140 if (QCPAxis::orientation(type) == Qt::Horizontal)
6142 for (int i=0; i<tickPositions.size(); ++i)
6143 painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6146 for (int i=0; i<tickPositions.size(); ++i)
6147 painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6152 if (!subTickPositions.isEmpty())
6154 painter->setPen(subTickPen);
6155 // direction of ticks ("inward" is right for left axis and left for right axis)
6156 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6157 if (QCPAxis::orientation(type) == Qt::Horizontal)
6159 for (int i=0; i<subTickPositions.size(); ++i)
6160 painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6163 for (int i=0; i<subTickPositions.size(); ++i)
6164 painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6167 margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6169 // draw axis base endings:
6170 bool antialiasingBackup = painter->antialiasing();
6171 painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6172 painter->setBrush(QBrush(basePen.color()));
6173 QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6174 if (lowerEnding.style() != QCPLineEnding::esNone)
6175 lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6176 if (upperEnding.style() != QCPLineEnding::esNone)
6177 upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6178 painter->setAntialiasing(antialiasingBackup);
6182 if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6184 oldClipRect = painter->clipRegion().boundingRect();
6185 painter->setClipRect(axisRect);
6187 QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6188 if (!tickLabels.isEmpty())
6190 if (tickLabelSide == QCPAxis::lsOutside)
6191 margin += tickLabelPadding;
6192 painter->setFont(tickLabelFont);
6193 painter->setPen(QPen(tickLabelColor));
6194 const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6195 int distanceToAxis = margin;
6196 if (tickLabelSide == QCPAxis::lsInside)
6197 distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6198 for (int i=0; i<maxLabelIndex; ++i)
6199 placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6200 if (tickLabelSide == QCPAxis::lsOutside)
6201 margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6203 if (tickLabelSide == QCPAxis::lsInside)
6204 painter->setClipRect(oldClipRect);
6208 if (!label.isEmpty())
6210 margin += labelPadding;
6211 painter->setFont(labelFont);
6212 painter->setPen(QPen(labelColor));
6213 labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6214 if (type == QCPAxis::atLeft)
6216 QTransform oldTransform = painter->transform();
6217 painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6218 painter->rotate(-90);
6219 painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6220 painter->setTransform(oldTransform);
6222 else if (type == QCPAxis::atRight)
6224 QTransform oldTransform = painter->transform();
6225 painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6226 painter->rotate(90);
6227 painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6228 painter->setTransform(oldTransform);
6230 else if (type == QCPAxis::atTop)
6231 painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6232 else if (type == QCPAxis::atBottom)
6233 painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6236 // set selection boxes:
6237 int selectionTolerance = 0;
6239 selectionTolerance = mParentPlot->selectionTolerance();
6241 qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6242 int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6243 int selAxisInSize = selectionTolerance;
6244 int selTickLabelSize;
6245 int selTickLabelOffset;
6246 if (tickLabelSide == QCPAxis::lsOutside)
6248 selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6249 selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6252 selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6253 selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6255 int selLabelSize = labelBounds.height();
6256 int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6257 if (type == QCPAxis::atLeft)
6259 mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
6260 mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
6261 mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
6262 } else if (type == QCPAxis::atRight)
6264 mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6265 mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6266 mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6267 } else if (type == QCPAxis::atTop)
6269 mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
6270 mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
6271 mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
6272 } else if (type == QCPAxis::atBottom)
6274 mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6275 mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6276 mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6278 mAxisSelectionBox = mAxisSelectionBox.normalized();
6279 mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
6280 mLabelSelectionBox = mLabelSelectionBox.normalized();
6281 // draw hitboxes for debug purposes:
6282 //painter->setBrush(Qt::NoBrush);
6283 //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6288 Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
6289 direction) needed to fit the axis.
6291 int QCPAxisPainterPrivate::size() const
6295 // get length of tick marks pointing outwards:
6296 if (!tickPositions.isEmpty())
6297 result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6299 // calculate size of tick labels:
6300 if (tickLabelSide == QCPAxis::lsOutside)
6302 QSize tickLabelsSize(0, 0);
6303 if (!tickLabels.isEmpty())
6305 for (int i=0; i<tickLabels.size(); ++i)
6306 getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6307 result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6308 result += tickLabelPadding;
6312 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6313 if (!label.isEmpty())
6315 QFontMetrics fontMetrics(labelFont);
6317 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6318 result += bounds.height() + labelPadding;
6326 Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
6327 method is called automatically in \ref draw, if any parameters have changed that invalidate the
6328 cached labels, such as font, color, etc.
6330 void QCPAxisPainterPrivate::clearCache()
6332 mLabelCache.clear();
6337 Returns a hash that allows uniquely identifying whether the label parameters have changed such
6338 that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
6339 return value of this method hasn't changed since the last redraw, the respective label parameters
6340 haven't changed and cached labels may be used.
6342 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
6345 result.append(QByteArray::number(tickLabelRotation));
6346 result.append(QByteArray::number((int)tickLabelSide));
6347 result.append(QByteArray::number((int)substituteExponent));
6348 result.append(QByteArray::number((int)numberMultiplyCross));
6349 result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6350 result.append(tickLabelFont.toString().toLatin1());
6356 Draws a single tick label with the provided \a painter, utilizing the internal label cache to
6357 significantly speed up drawing of labels that were drawn in previous calls. The tick label is
6358 always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
6359 pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
6360 for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
6361 at which the label should be drawn.
6363 In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
6364 largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
6365 drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
6366 tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
6369 The label is drawn with the font and pen that are currently set on the \a painter. To draw
6370 superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
6373 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6375 // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6376 if (text.isEmpty()) return;
6378 QPointF labelAnchor;
6381 case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6382 case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6383 case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6384 case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6386 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6388 CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
6389 if (!cachedLabel) // no cached label existed, create it
6391 cachedLabel = new CachedLabel;
6392 TickLabelData labelData = getTickLabelData(painter->font(), text);
6393 cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6394 cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6395 cachedLabel->pixmap.fill(Qt::transparent);
6396 QCPPainter cachePainter(&cachedLabel->pixmap);
6397 cachePainter.setPen(painter->pen());
6398 drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6400 // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6401 bool labelClippedByBorder = false;
6402 if (tickLabelSide == QCPAxis::lsOutside)
6404 if (QCPAxis::orientation(type) == Qt::Horizontal)
6405 labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
6407 labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
6409 if (!labelClippedByBorder)
6411 painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6412 finalSize = cachedLabel->pixmap.size();
6414 mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
6415 } else // label caching disabled, draw text directly on surface:
6417 TickLabelData labelData = getTickLabelData(painter->font(), text);
6418 QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6419 // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6420 bool labelClippedByBorder = false;
6421 if (tickLabelSide == QCPAxis::lsOutside)
6423 if (QCPAxis::orientation(type) == Qt::Horizontal)
6424 labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
6426 labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
6428 if (!labelClippedByBorder)
6430 drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6431 finalSize = labelData.rotatedTotalBounds.size();
6435 // expand passed tickLabelsSize if current tick label is larger:
6436 if (finalSize.width() > tickLabelsSize->width())
6437 tickLabelsSize->setWidth(finalSize.width());
6438 if (finalSize.height() > tickLabelsSize->height())
6439 tickLabelsSize->setHeight(finalSize.height());
6444 This is a \ref placeTickLabel helper function.
6446 Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
6447 y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
6448 directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
6449 QCP::phCacheLabels plotting hint is not set.
6451 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6453 // backup painter settings that we're about to change:
6454 QTransform oldTransform = painter->transform();
6455 QFont oldFont = painter->font();
6457 // transform painter to position/rotation:
6458 painter->translate(x, y);
6459 if (!qFuzzyIsNull(tickLabelRotation))
6460 painter->rotate(tickLabelRotation);
6463 if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6465 painter->setFont(labelData.baseFont);
6466 painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6467 painter->setFont(labelData.expFont);
6468 painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6471 painter->setFont(labelData.baseFont);
6472 painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6475 // reset painter settings to what it was before:
6476 painter->setTransform(oldTransform);
6477 painter->setFont(oldFont);
6482 This is a \ref placeTickLabel helper function.
6484 Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
6485 processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
6486 exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
6488 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
6490 TickLabelData result;
6492 // determine whether beautiful decimal powers should be used
6493 bool useBeautifulPowers = false;
6494 int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
6495 int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
6496 if (substituteExponent)
6498 ePos = text.indexOf(QLatin1Char('e'));
6499 if (ePos > 0 && text.at(ePos-1).isDigit())
6502 while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
6504 if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
6505 useBeautifulPowers = true;
6509 // calculate text bounding rects and do string preparation for beautiful decimal powers:
6510 result.baseFont = font;
6511 if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
6512 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
6513 if (useBeautifulPowers)
6515 // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6516 result.basePart = text.left(ePos);
6517 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6518 if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6519 result.basePart = QLatin1String("10");
6521 result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6522 result.expPart = text.mid(ePos+1);
6523 // clip "+" and leading zeros off expPart:
6524 while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6525 result.expPart.remove(1, 1);
6526 if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6527 result.expPart.remove(0, 1);
6528 // prepare smaller font for exponent:
6529 result.expFont = font;
6530 if (result.expFont.pointSize() > 0)
6531 result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6533 result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
6534 // calculate bounding rects of base part, exponent part and total one:
6535 result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6536 result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6537 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
6538 } else // useBeautifulPowers == false
6540 result.basePart = text;
6541 result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6543 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
6545 // calculate possibly different bounding rect after rotation:
6546 result.rotatedTotalBounds = result.totalBounds;
6547 if (!qFuzzyIsNull(tickLabelRotation))
6549 QTransform transform;
6550 transform.rotate(tickLabelRotation);
6551 result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6559 This is a \ref placeTickLabel helper function.
6561 Calculates the offset at which the top left corner of the specified tick label shall be drawn.
6562 The offset is relative to a point right next to the tick the label belongs to.
6564 This function is thus responsible for e.g. centering tick labels under ticks and positioning them
6565 appropriately when they are rotated.
6567 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
6570 calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6571 explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6572 horizontally under the corresponding tick is always on the label side that is closer to the
6573 axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6574 is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6575 will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6576 time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6579 bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6580 bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6581 double radians = tickLabelRotation/180.0*M_PI;
6583 if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6587 if (tickLabelRotation > 0)
6589 x = -qCos(radians)*labelData.totalBounds.width();
6590 y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6593 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6594 y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6598 x = -labelData.totalBounds.width();
6599 y = -labelData.totalBounds.height()/2.0;
6601 } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6605 if (tickLabelRotation > 0)
6607 x = +qSin(radians)*labelData.totalBounds.height();
6608 y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6612 y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6617 y = -labelData.totalBounds.height()/2.0;
6619 } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6623 if (tickLabelRotation > 0)
6625 x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6626 y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6629 x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6630 y = -qCos(-radians)*labelData.totalBounds.height();
6634 x = -labelData.totalBounds.width()/2.0;
6635 y = -labelData.totalBounds.height();
6637 } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6641 if (tickLabelRotation > 0)
6643 x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6647 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6648 y = +qSin(-radians)*labelData.totalBounds.width();
6652 x = -labelData.totalBounds.width()/2.0;
6657 return QPointF(x, y);
6662 Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
6663 to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
6664 margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
6665 smaller width/height.
6667 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6669 // note: this function must return the same tick label sizes as the placeTickLabel function.
6671 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6673 const CachedLabel *cachedLabel = mLabelCache.object(text);
6674 finalSize = cachedLabel->pixmap.size();
6675 } else // label caching disabled or no label with this text cached:
6677 TickLabelData labelData = getTickLabelData(font, text);
6678 finalSize = labelData.rotatedTotalBounds.size();
6681 // expand passed tickLabelsSize if current tick label is larger:
6682 if (finalSize.width() > tickLabelsSize->width())
6683 tickLabelsSize->setWidth(finalSize.width());
6684 if (finalSize.height() > tickLabelsSize->height())
6685 tickLabelsSize->setHeight(finalSize.height());
6689 ////////////////////////////////////////////////////////////////////////////////////////////////////
6690 //////////////////// QCPAbstractPlottable
6691 ////////////////////////////////////////////////////////////////////////////////////////////////////
6693 /*! \class QCPAbstractPlottable
6694 \brief The abstract base class for all data representing objects in a plot.
6696 It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
6697 abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to
6698 create new ways of displaying data (see "Creating own plottables" below).
6700 All further specifics are in the subclasses, for example:
6701 \li A normal graph with possibly a line, scatter points and error bars: \ref QCPGraph
6702 (typically created with \ref QCustomPlot::addGraph)
6703 \li A parametric curve: \ref QCPCurve
6704 \li A bar chart: \ref QCPBars
6705 \li A statistical box plot: \ref QCPStatisticalBox
6706 \li A color encoded two-dimensional map: \ref QCPColorMap
6707 \li An OHLC/Candlestick chart: \ref QCPFinancial
6709 \section plottables-subclassing Creating own plottables
6711 To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
6712 virtual functions, you must implement:
6716 \li \ref drawLegendIcon
6717 \li \ref getKeyRange
6718 \li \ref getValueRange
6720 See the documentation of those functions for what they need to do.
6722 For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
6723 coordinates to pixel coordinates. This function is quite convenient, because it takes the
6724 orientation of the key and value axes into account for you (x and y are swapped when the key axis
6725 is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
6726 to translate many points in a loop like QCPGraph), you can directly use \ref
6727 QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
6730 Here are some important members you inherit from QCPAbstractPlottable:
6733 <td>QCustomPlot *\b mParentPlot</td>
6734 <td>A pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.</td>
6736 <td>QString \b mName</td>
6737 <td>The name of the plottable.</td>
6739 <td>QPen \b mPen</td>
6740 <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>
6742 <td>QPen \b mSelectedPen</td>
6743 <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>
6745 <td>QBrush \b mBrush</td>
6746 <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>
6748 <td>QBrush \b mSelectedBrush</td>
6749 <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>
6751 <td>QPointer<QCPAxis>\b mKeyAxis, \b mValueAxis</td>
6752 <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.
6753 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>
6755 <td>bool \b mSelected</td>
6756 <td>indicates whether the plottable is selected or not.</td>
6761 /* start of documentation of pure virtual functions */
6763 /*! \fn void QCPAbstractPlottable::clearData() = 0
6764 Clears all data in the plottable.
6767 /*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
6770 called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
6771 of this plottable inside \a rect, next to the plottable name.
6773 The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't
6774 appear outside the legend icon border.
6777 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
6780 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6781 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6782 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6783 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6784 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6785 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6786 use the returned range (e.g. no points in data).
6788 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6789 this function may have size zero, which wouldn't count as a valid range.
6791 \see rescaleAxes, getValueRange
6794 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
6797 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6798 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6799 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6800 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6801 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6802 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6803 use the returned range (e.g. no points in data).
6805 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6806 this function may have size zero, which wouldn't count as a valid range.
6808 \see rescaleAxes, getKeyRange
6811 /* end of documentation of pure virtual functions */
6812 /* start of documentation of signals */
6814 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
6816 This signal is emitted when the selection state of this plottable has changed, either by user
6817 interaction or by a direct call to \ref setSelected.
6820 /*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
6822 This signal is emitted when the selectability of this plottable has changed.
6827 /* end of documentation of signals */
6830 Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
6831 its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
6832 and have perpendicular orientations. If either of these restrictions is violated, a corresponding
6833 message is printed to the debug output (qDebug), the construction is not aborted, though.
6835 Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables,
6836 it can't be directly instantiated.
6838 You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead.
6840 QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
6841 QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6843 mAntialiasedFill(true),
6844 mAntialiasedScatters(true),
6845 mAntialiasedErrorBars(false),
6847 mSelectedPen(Qt::black),
6848 mBrush(Qt::NoBrush),
6849 mSelectedBrush(Qt::NoBrush),
6851 mValueAxis(valueAxis),
6855 if (keyAxis->parentPlot() != valueAxis->parentPlot())
6856 qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6857 if (keyAxis->orientation() == valueAxis->orientation())
6858 qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6862 The name is the textual representation of this plottable as it is displayed in the legend
6863 (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
6865 void QCPAbstractPlottable::setName(const QString &name)
6871 Sets whether fills of this plottable are drawn antialiased or not.
6873 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6874 QCustomPlot::setNotAntialiasedElements.
6876 void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
6878 mAntialiasedFill = enabled;
6882 Sets whether the scatter symbols of this plottable are drawn antialiased or not.
6884 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6885 QCustomPlot::setNotAntialiasedElements.
6887 void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
6889 mAntialiasedScatters = enabled;
6893 Sets whether the error bars of this plottable are drawn antialiased or not.
6895 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6896 QCustomPlot::setNotAntialiasedElements.
6898 void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
6900 mAntialiasedErrorBars = enabled;
6905 The pen is used to draw basic lines that make up the plottable representation in the
6908 For example, the \ref QCPGraph subclass draws its graph lines with this pen.
6912 void QCPAbstractPlottable::setPen(const QPen &pen)
6918 When the plottable is selected, this pen is used to draw basic lines instead of the normal
6919 pen set via \ref setPen.
6921 \see setSelected, setSelectable, setSelectedBrush, selectTest
6923 void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
6929 The brush is used to draw basic fills of the plottable representation in the
6930 plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
6932 For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
6933 it's not set to Qt::NoBrush.
6937 void QCPAbstractPlottable::setBrush(const QBrush &brush)
6943 When the plottable is selected, this brush is used to draw fills instead of the normal
6944 brush set via \ref setBrush.
6946 \see setSelected, setSelectable, setSelectedPen, selectTest
6948 void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
6950 mSelectedBrush = brush;
6954 The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
6955 to the plottable's value axis. This function performs no checks to make sure this is the case.
6956 The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
6957 y-axis (QCustomPlot::yAxis) as value axis.
6959 Normally, the key and value axes are set in the constructor of the plottable (or \ref
6960 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6964 void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
6970 The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
6971 orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
6972 case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
6973 the y-axis (QCustomPlot::yAxis) as value axis.
6975 Normally, the key and value axes are set in the constructor of the plottable (or \ref
6976 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6980 void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
6986 Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
6987 (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
6989 However, even when \a selectable was set to false, it is possible to set the selection manually,
6990 by calling \ref setSelected directly.
6994 void QCPAbstractPlottable::setSelectable(bool selectable)
6996 if (mSelectable != selectable)
6998 mSelectable = selectable;
6999 emit selectableChanged(mSelectable);
7004 Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
7005 to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
7007 The entire selection mechanism for plottables is handled automatically when \ref
7008 QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
7009 you wish to change the selection state manually.
7011 This function can change the selection state even when \ref setSelectable was set to false.
7013 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
7015 \see setSelectable, selectTest
7017 void QCPAbstractPlottable::setSelected(bool selected)
7019 if (mSelected != selected)
7021 mSelected = selected;
7022 emit selectionChanged(mSelected);
7027 Rescales the key and value axes associated with this plottable to contain all displayed data, so
7028 the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
7029 sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
7030 Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
7031 outside of that domain.
7033 \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
7034 multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
7035 \a onlyEnlarge set to false (the default), and all subsequent set to true.
7037 \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale
7039 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7041 rescaleKeyAxis(onlyEnlarge);
7042 rescaleValueAxis(onlyEnlarge);
7046 Rescales the key axis of the plottable so the whole plottable is visible.
7048 See \ref rescaleAxes for detailed behaviour.
7050 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7052 QCPAxis *keyAxis = mKeyAxis.data();
7053 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7055 SignDomain signDomain = sdBoth;
7056 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7057 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7060 QCPRange newRange = getKeyRange(foundRange, signDomain);
7064 newRange.expand(keyAxis->range());
7065 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
7067 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
7068 if (keyAxis->scaleType() == QCPAxis::stLinear)
7070 newRange.lower = center-keyAxis->range().size()/2.0;
7071 newRange.upper = center+keyAxis->range().size()/2.0;
7072 } else // scaleType() == stLogarithmic
7074 newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7075 newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7078 keyAxis->setRange(newRange);
7083 Rescales the value axis of the plottable so the whole plottable is visible.
7085 Returns true if the axis was actually scaled. This might not be the case if this plottable has an
7086 invalid range, e.g. because it has no data points.
7088 See \ref rescaleAxes for detailed behaviour.
7090 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7092 QCPAxis *valueAxis = mValueAxis.data();
7093 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7095 SignDomain signDomain = sdBoth;
7096 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7097 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7100 QCPRange newRange = getValueRange(foundRange, signDomain);
7104 newRange.expand(valueAxis->range());
7105 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
7107 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
7108 if (valueAxis->scaleType() == QCPAxis::stLinear)
7110 newRange.lower = center-valueAxis->range().size()/2.0;
7111 newRange.upper = center+valueAxis->range().size()/2.0;
7112 } else // scaleType() == stLogarithmic
7114 newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7115 newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7118 valueAxis->setRange(newRange);
7123 Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
7125 Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
7126 needs a more specialized representation in the legend, this function will take this into account
7127 and instead create the specialized subclass of QCPAbstractLegendItem.
7129 Returns true on success, i.e. when the legend exists and a legend item associated with this plottable isn't already in
7132 \see removeFromLegend, QCPLegend::addItem
7134 bool QCPAbstractPlottable::addToLegend()
7136 if (!mParentPlot || !mParentPlot->legend)
7139 if (!mParentPlot->legend->hasItemWithPlottable(this))
7141 mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
7148 Removes the plottable from the legend of the parent QCustomPlot. This means the
7149 QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
7152 Returns true on success, i.e. if the legend exists and a legend item associated with this
7153 plottable was found and removed.
7155 \see addToLegend, QCPLegend::removeItem
7157 bool QCPAbstractPlottable::removeFromLegend() const
7159 if (!mParentPlot->legend)
7162 if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
7163 return mParentPlot->legend->removeItem(lip);
7168 /* inherits documentation from base class */
7169 QRect QCPAbstractPlottable::clipRect() const
7171 if (mKeyAxis && mValueAxis)
7172 return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7177 /* inherits documentation from base class */
7178 QCP::Interaction QCPAbstractPlottable::selectionCategory() const
7180 return QCP::iSelectPlottables;
7185 Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
7186 taking the orientations of the axes associated with this plottable into account (e.g. whether key
7189 \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
7191 \see pixelsToCoords, QCPAxis::coordToPixel
7193 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7195 QCPAxis *keyAxis = mKeyAxis.data();
7196 QCPAxis *valueAxis = mValueAxis.data();
7197 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7199 if (keyAxis->orientation() == Qt::Horizontal)
7201 x = keyAxis->coordToPixel(key);
7202 y = valueAxis->coordToPixel(value);
7205 y = keyAxis->coordToPixel(key);
7206 x = valueAxis->coordToPixel(value);
7213 Returns the input as pixel coordinates in a QPointF.
7215 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7217 QCPAxis *keyAxis = mKeyAxis.data();
7218 QCPAxis *valueAxis = mValueAxis.data();
7219 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7221 if (keyAxis->orientation() == Qt::Horizontal)
7222 return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7224 return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7229 Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
7230 taking the orientations of the axes associated with this plottable into account (e.g. whether key
7233 \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
7235 \see coordsToPixels, QCPAxis::coordToPixel
7237 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7239 QCPAxis *keyAxis = mKeyAxis.data();
7240 QCPAxis *valueAxis = mValueAxis.data();
7241 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7243 if (keyAxis->orientation() == Qt::Horizontal)
7245 key = keyAxis->pixelToCoord(x);
7246 value = valueAxis->pixelToCoord(y);
7249 key = keyAxis->pixelToCoord(y);
7250 value = valueAxis->pixelToCoord(x);
7257 Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
7259 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7261 pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7266 Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
7267 graph is not selected and mSelectedPen when it is.
7269 QPen QCPAbstractPlottable::mainPen() const
7271 return mSelected ? mSelectedPen : mPen;
7276 Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
7277 graph is not selected and mSelectedBrush when it is.
7279 QBrush QCPAbstractPlottable::mainBrush() const
7281 return mSelected ? mSelectedBrush : mBrush;
7286 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7287 before drawing plottable lines.
7289 This is the antialiasing state the painter passed to the \ref draw method is in by default.
7291 This function takes into account the local setting of the antialiasing flag as well as the
7292 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7293 QCustomPlot::setNotAntialiasedElements.
7295 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7297 void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
7299 applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
7304 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7305 before drawing plottable fills.
7307 This function takes into account the local setting of the antialiasing flag as well as the
7308 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7309 QCustomPlot::setNotAntialiasedElements.
7311 \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7313 void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
7315 applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
7320 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7321 before drawing plottable scatter points.
7323 This function takes into account the local setting of the antialiasing flag as well as the
7324 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7325 QCustomPlot::setNotAntialiasedElements.
7327 \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
7329 void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
7331 applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
7336 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7337 before drawing plottable error bars.
7339 This function takes into account the local setting of the antialiasing flag as well as the
7340 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7341 QCustomPlot::setNotAntialiasedElements.
7343 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
7345 void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
7347 applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
7352 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
7355 This function may be used to help with the implementation of the \ref selectTest function for
7356 specific plottables.
7358 \note This function is identical to QCPAbstractItem::distSqrToLine
7360 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7367 double vLengthSqr = v.lengthSquared();
7368 if (!qFuzzyIsNull(vLengthSqr))
7370 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7372 return (a-p).lengthSquared();
7374 return (b-p).lengthSquared();
7376 return ((a + mu*v)-p).lengthSquared();
7378 return (a-p).lengthSquared();
7381 /* inherits documentation from base class */
7382 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7388 bool selBefore = mSelected;
7389 setSelected(additive ? !mSelected : true);
7390 if (selectionStateChanged)
7391 *selectionStateChanged = mSelected != selBefore;
7395 /* inherits documentation from base class */
7396 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7400 bool selBefore = mSelected;
7402 if (selectionStateChanged)
7403 *selectionStateChanged = mSelected != selBefore;
7408 ////////////////////////////////////////////////////////////////////////////////////////////////////
7409 //////////////////// QCPItemAnchor
7410 ////////////////////////////////////////////////////////////////////////////////////////////////////
7412 /*! \class QCPItemAnchor
7413 \brief An anchor of an item to which positions can be attached to.
7415 An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
7416 control anything on its item, but provides a way to tie other items via their positions to the
7419 For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
7420 Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
7421 attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
7422 calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
7423 QCPItemRect. This way the start of the line will now always follow the respective anchor location
7426 Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
7427 anchor to other positions.
7429 To learn how to provide anchors in your own item subclasses, see the subclassing section of the
7430 QCPAbstractItem documentation.
7433 /* start documentation of inline functions */
7435 /*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
7437 Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if
7438 it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
7440 This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids
7441 dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with
7445 /* end documentation of inline functions */
7448 Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
7449 you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
7450 explained in the subclassing section of the QCPAbstractItem documentation.
7452 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7454 mParentPlot(parentPlot),
7455 mParentItem(parentItem),
7460 QCPItemAnchor::~QCPItemAnchor()
7462 // unregister as parent at children:
7463 foreach (QCPItemPosition *child, mChildrenX.toList())
7465 if (child->parentAnchorX() == this)
7466 child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7468 foreach (QCPItemPosition *child, mChildrenY.toList())
7470 if (child->parentAnchorY() == this)
7471 child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7476 Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
7478 The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
7479 parent item, QCPItemAnchor is just an intermediary.
7481 QPointF QCPItemAnchor::pixelPoint() const
7487 return mParentItem->anchorPixelPoint(mAnchorId);
7490 qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7495 qDebug() << Q_FUNC_INFO << "no parent item set";
7502 Adds \a pos to the childX list of this anchor, which keeps track of which children use this
7503 anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7504 prior to destruction of the anchor.
7506 Note that this function does not change the parent setting in \a pos.
7508 void QCPItemAnchor::addChildX(QCPItemPosition *pos)
7510 if (!mChildrenX.contains(pos))
7511 mChildrenX.insert(pos);
7513 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7518 Removes \a pos from the childX list of this anchor.
7520 Note that this function does not change the parent setting in \a pos.
7522 void QCPItemAnchor::removeChildX(QCPItemPosition *pos)
7524 if (!mChildrenX.remove(pos))
7525 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7530 Adds \a pos to the childY list of this anchor, which keeps track of which children use this
7531 anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7532 prior to destruction of the anchor.
7534 Note that this function does not change the parent setting in \a pos.
7536 void QCPItemAnchor::addChildY(QCPItemPosition *pos)
7538 if (!mChildrenY.contains(pos))
7539 mChildrenY.insert(pos);
7541 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7546 Removes \a pos from the childY list of this anchor.
7548 Note that this function does not change the parent setting in \a pos.
7550 void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
7552 if (!mChildrenY.remove(pos))
7553 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7557 ////////////////////////////////////////////////////////////////////////////////////////////////////
7558 //////////////////// QCPItemPosition
7559 ////////////////////////////////////////////////////////////////////////////////////////////////////
7561 /*! \class QCPItemPosition
7562 \brief Manages the position of an item.
7564 Every item has at least one public QCPItemPosition member pointer which provides ways to position the
7565 item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
7566 \a topLeft and \a bottomRight.
7568 QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type
7569 defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel
7570 coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also
7571 possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref
7572 setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y
7573 direction, while following a plot coordinate in the X direction.
7575 A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie
7576 multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords)
7577 are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0)
7578 means directly ontop of the parent anchor. For example, You could attach the \a start position of
7579 a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line
7580 always be centered under the text label, no matter where the text is moved to. For more advanced
7581 plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see
7582 \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X
7583 direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B
7586 Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent
7587 anchor for other positions.
7589 To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
7590 works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
7591 setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
7595 /* start documentation of inline functions */
7597 /*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const
7599 Returns the current position type.
7601 If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the
7602 type of the X coordinate. In that case rather use \a typeX() and \a typeY().
7607 /*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const
7609 Returns the current parent anchor.
7611 If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY),
7612 this method returns the parent anchor of the Y coordinate. In that case rather use \a
7613 parentAnchorX() and \a parentAnchorY().
7615 \see setParentAnchor
7618 /* end documentation of inline functions */
7621 Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
7622 you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
7623 explained in the subclassing section of the QCPAbstractItem documentation.
7625 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7626 QCPItemAnchor(parentPlot, parentItem, name),
7627 mPositionTypeX(ptAbsolute),
7628 mPositionTypeY(ptAbsolute),
7636 QCPItemPosition::~QCPItemPosition()
7638 // unregister as parent at children:
7639 // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7640 // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7641 foreach (QCPItemPosition *child, mChildrenX.toList())
7643 if (child->parentAnchorX() == this)
7644 child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7646 foreach (QCPItemPosition *child, mChildrenY.toList())
7648 if (child->parentAnchorY() == this)
7649 child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7651 // unregister as child in parent:
7653 mParentAnchorX->removeChildX(this);
7655 mParentAnchorY->removeChildY(this);
7658 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7659 QCPAxisRect *QCPItemPosition::axisRect() const
7661 return mAxisRect.data();
7665 Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
7666 should be handled and how the QCPItemPosition should behave in the plot.
7668 The possible values for \a type can be separated in two main categories:
7670 \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
7671 and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
7672 By default, the QCustomPlot's x- and yAxis are used.
7674 \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This
7675 corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref
7676 ptAxisRectRatio. They differ only in the way the absolute position is described, see the
7677 documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify
7678 the axis rect with \ref setAxisRect. By default this is set to the main axis rect.
7680 Note that the position type \ref ptPlotCoords is only available (and sensible) when the position
7681 has no parent anchor (\ref setParentAnchor).
7683 If the type is changed, the apparent pixel position on the plot is preserved. This means
7684 the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
7686 This method sets the type for both X and Y directions. It is also possible to set different types
7687 for X and Y, see \ref setTypeX, \ref setTypeY.
7689 void QCPItemPosition::setType(QCPItemPosition::PositionType type)
7696 This method sets the position type of the X coordinate to \a type.
7698 For a detailed description of what a position type is, see the documentation of \ref setType.
7700 \see setType, setTypeY
7702 void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)
7704 if (mPositionTypeX != type)
7706 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7707 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7708 bool retainPixelPosition = true;
7709 if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7710 retainPixelPosition = false;
7711 if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7712 retainPixelPosition = false;
7715 if (retainPixelPosition)
7716 pixel = pixelPoint();
7718 mPositionTypeX = type;
7720 if (retainPixelPosition)
7721 setPixelPoint(pixel);
7726 This method sets the position type of the Y coordinate to \a type.
7728 For a detailed description of what a position type is, see the documentation of \ref setType.
7730 \see setType, setTypeX
7732 void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
7734 if (mPositionTypeY != type)
7736 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7737 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7738 bool retainPixelPosition = true;
7739 if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7740 retainPixelPosition = false;
7741 if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7742 retainPixelPosition = false;
7745 if (retainPixelPosition)
7746 pixel = pixelPoint();
7748 mPositionTypeY = type;
7750 if (retainPixelPosition)
7751 setPixelPoint(pixel);
7756 Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
7757 follow any position changes of the anchor. The local coordinate system of positions with a parent
7758 anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence
7759 the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.)
7761 if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
7762 during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
7763 will be exactly on top of the parent anchor.
7765 To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
7767 If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is
7768 set to \ref ptAbsolute, to keep the position in a valid state.
7770 This method sets the parent anchor for both X and Y directions. It is also possible to set
7771 different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY.
7773 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7775 bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7776 bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7777 return successX && successY;
7781 This method sets the parent anchor of the X coordinate to \a parentAnchor.
7783 For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7785 \see setParentAnchor, setParentAnchorY
7787 bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7789 // make sure self is not assigned as parent:
7790 if (parentAnchor == this)
7792 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7795 // make sure no recursive parent-child-relationships are created:
7796 QCPItemAnchor *currentParent = parentAnchor;
7797 while (currentParent)
7799 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7801 // is a QCPItemPosition, might have further parent, so keep iterating
7802 if (currentParentPos == this)
7804 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7807 currentParent = currentParentPos->parentAnchorX();
7810 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7811 // same, to prevent a position being child of an anchor which itself depends on the position,
7812 // because they're both on the same item:
7813 if (currentParent->mParentItem == mParentItem)
7815 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7822 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7823 if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)
7824 setTypeX(ptAbsolute);
7826 // save pixel position:
7828 if (keepPixelPosition)
7829 pixelP = pixelPoint();
7830 // unregister at current parent anchor:
7832 mParentAnchorX->removeChildX(this);
7833 // register at new parent anchor:
7835 parentAnchor->addChildX(this);
7836 mParentAnchorX = parentAnchor;
7837 // restore pixel position under new parent:
7838 if (keepPixelPosition)
7839 setPixelPoint(pixelP);
7841 setCoords(0, coords().y());
7846 This method sets the parent anchor of the Y coordinate to \a parentAnchor.
7848 For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7850 \see setParentAnchor, setParentAnchorX
7852 bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7854 // make sure self is not assigned as parent:
7855 if (parentAnchor == this)
7857 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7860 // make sure no recursive parent-child-relationships are created:
7861 QCPItemAnchor *currentParent = parentAnchor;
7862 while (currentParent)
7864 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7866 // is a QCPItemPosition, might have further parent, so keep iterating
7867 if (currentParentPos == this)
7869 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7872 currentParent = currentParentPos->parentAnchorY();
7875 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7876 // same, to prevent a position being child of an anchor which itself depends on the position,
7877 // because they're both on the same item:
7878 if (currentParent->mParentItem == mParentItem)
7880 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7887 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7888 if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)
7889 setTypeY(ptAbsolute);
7891 // save pixel position:
7893 if (keepPixelPosition)
7894 pixelP = pixelPoint();
7895 // unregister at current parent anchor:
7897 mParentAnchorY->removeChildY(this);
7898 // register at new parent anchor:
7900 parentAnchor->addChildY(this);
7901 mParentAnchorY = parentAnchor;
7902 // restore pixel position under new parent:
7903 if (keepPixelPosition)
7904 setPixelPoint(pixelP);
7906 setCoords(coords().x(), 0);
7911 Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
7912 (\ref setType, \ref setTypeX, \ref setTypeY).
7914 For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
7915 on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the
7916 QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the
7917 plot coordinate system defined by the axes set by \ref setAxes. By default those are the
7918 QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available
7919 coordinate types and their meaning.
7921 If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a
7922 value must also be provided in the different coordinate systems. Here, the X type refers to \a
7923 key, and the Y type refers to \a value.
7927 void QCPItemPosition::setCoords(double key, double value)
7935 Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
7936 meaning of \a value of the \ref setCoords(double key, double value) method.
7938 void QCPItemPosition::setCoords(const QPointF &pos)
7940 setCoords(pos.x(), pos.y());
7944 Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
7945 includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
7949 QPointF QCPItemPosition::pixelPoint() const
7954 switch (mPositionTypeX)
7960 result.rx() += mParentAnchorX->pixelPoint().x();
7963 case ptViewportRatio:
7965 result.rx() = mKey*mParentPlot->viewport().width();
7967 result.rx() += mParentAnchorX->pixelPoint().x();
7969 result.rx() += mParentPlot->viewport().left();
7972 case ptAxisRectRatio:
7976 result.rx() = mKey*mAxisRect.data()->width();
7978 result.rx() += mParentAnchorX->pixelPoint().x();
7980 result.rx() += mAxisRect.data()->left();
7982 qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
7987 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
7988 result.rx() = mKeyAxis.data()->coordToPixel(mKey);
7989 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
7990 result.rx() = mValueAxis.data()->coordToPixel(mValue);
7992 qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
7998 switch (mPositionTypeY)
8002 result.ry() = mValue;
8004 result.ry() += mParentAnchorY->pixelPoint().y();
8007 case ptViewportRatio:
8009 result.ry() = mValue*mParentPlot->viewport().height();
8011 result.ry() += mParentAnchorY->pixelPoint().y();
8013 result.ry() += mParentPlot->viewport().top();
8016 case ptAxisRectRatio:
8020 result.ry() = mValue*mAxisRect.data()->height();
8022 result.ry() += mParentAnchorY->pixelPoint().y();
8024 result.ry() += mAxisRect.data()->top();
8026 qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8031 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8032 result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8033 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8034 result.ry() = mValueAxis.data()->coordToPixel(mValue);
8036 qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8045 When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the
8046 coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and
8047 yAxis of the QCustomPlot.
8049 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
8052 mValueAxis = valueAxis;
8056 When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the
8057 coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of
8060 void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)
8062 mAxisRect = axisRect;
8066 Sets the apparent pixel position. This works no matter what type (\ref setType) this
8067 QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed
8068 appropriately, to make the position finally appear at the specified pixel values.
8070 Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is
8071 identical to that of \ref setCoords.
8073 \see pixelPoint, setCoords
8075 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
8077 double x = pixelPoint.x();
8078 double y = pixelPoint.y();
8080 switch (mPositionTypeX)
8085 x -= mParentAnchorX->pixelPoint().x();
8088 case ptViewportRatio:
8091 x -= mParentAnchorX->pixelPoint().x();
8093 x -= mParentPlot->viewport().left();
8094 x /= (double)mParentPlot->viewport().width();
8097 case ptAxisRectRatio:
8102 x -= mParentAnchorX->pixelPoint().x();
8104 x -= mAxisRect.data()->left();
8105 x /= (double)mAxisRect.data()->width();
8107 qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8112 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8113 x = mKeyAxis.data()->pixelToCoord(x);
8114 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8115 y = mValueAxis.data()->pixelToCoord(x);
8117 qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8122 switch (mPositionTypeY)
8127 y -= mParentAnchorY->pixelPoint().y();
8130 case ptViewportRatio:
8133 y -= mParentAnchorY->pixelPoint().y();
8135 y -= mParentPlot->viewport().top();
8136 y /= (double)mParentPlot->viewport().height();
8139 case ptAxisRectRatio:
8144 y -= mParentAnchorY->pixelPoint().y();
8146 y -= mAxisRect.data()->top();
8147 y /= (double)mAxisRect.data()->height();
8149 qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8154 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8155 x = mKeyAxis.data()->pixelToCoord(y);
8156 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8157 y = mValueAxis.data()->pixelToCoord(y);
8159 qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8168 ////////////////////////////////////////////////////////////////////////////////////////////////////
8169 //////////////////// QCPAbstractItem
8170 ////////////////////////////////////////////////////////////////////////////////////////////////////
8172 /*! \class QCPAbstractItem
8173 \brief The abstract base class for all items in a plot.
8175 In QCustomPlot, items are supplemental graphical elements that are neither plottables
8176 (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
8177 plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
8178 specific item has at least one QCPItemPosition member which controls the positioning. Some items
8179 are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
8180 example, QCPItemRect has \a topLeft and \a bottomRight).
8182 This abstract base class defines a very basic interface like visibility and clipping. Since this
8183 class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
8184 yourself to create new items.
8186 The built-in items are:
8188 <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>
8189 <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>
8190 <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>
8191 <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
8192 <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
8193 <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
8194 <tr><td>QCPItemText</td><td>A text label</td></tr>
8195 <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
8196 <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>
8199 \section items-clipping Clipping
8201 Items are by default clipped to the main axis rect (they are only visible inside the axis rect).
8202 To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect
8203 "setClipToAxisRect(false)".
8205 On the other hand if you want the item to be clipped to a different axis rect, specify it via
8206 \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and
8207 in principle is independent of the coordinate axes the item might be tied to via its position
8208 members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping
8209 also contains the axes used for the item positions.
8211 \section items-using Using items
8213 First you instantiate the item you want to use and add it to the plot:
8214 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1
8215 by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
8216 set the plot coordinates where the line should start/end:
8217 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2
8218 If we don't want the line to be positioned in plot coordinates but a different coordinate system,
8219 e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this:
8220 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3
8221 Then we can set the coordinates, this time in pixels:
8222 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4
8223 and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect:
8224 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5
8226 For more advanced plots, it is even possible to set different types and parent anchors per X/Y
8227 coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref
8228 QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition.
8230 \section items-subclassing Creating own items
8232 To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
8233 virtual functions, you must implement:
8237 See the documentation of those functions for what they need to do.
8239 \subsection items-positioning Allowing the item to be positioned
8241 As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
8242 have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add
8243 a public member of type QCPItemPosition like so:
8245 \code QCPItemPosition * const myPosition;\endcode
8247 the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
8248 instance it points to, can be modified, of course).
8249 The initialization of this pointer is made easy with the \ref createPosition function. Just assign
8250 the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
8251 takes a string which is the name of the position, typically this is identical to the variable name.
8252 For example, the constructor of QCPItemExample could look like this:
8255 QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
8256 QCPAbstractItem(parentPlot),
8257 myPosition(createPosition("myPosition"))
8259 // other constructor code
8263 \subsection items-drawing The draw function
8265 To give your item a visual representation, reimplement the \ref draw function and use the passed
8266 QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the
8267 position member(s) via \ref QCPItemPosition::pixelPoint.
8269 To optimize performance you should calculate a bounding rect first (don't forget to take the pen
8270 width into account), check whether it intersects the \ref clipRect, and only draw the item at all
8271 if this is the case.
8273 \subsection items-selection The selectTest function
8275 Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
8276 \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
8277 simpler for most items. See the documentation of \ref selectTest for what the function parameters
8278 mean and what the function should return.
8280 \subsection anchors Providing anchors
8282 Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
8285 \code QCPItemAnchor * const bottom;\endcode
8287 and create it in the constructor with the \ref createAnchor function, assigning it a name and an
8288 anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
8289 Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
8290 provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
8293 In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
8294 position when anything attached to the anchor needs to know the coordinates.
8297 /* start of documentation of inline functions */
8299 /*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
8301 Returns all positions of the item in a list.
8303 \see anchors, position
8306 /*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
8308 Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
8309 also an anchor, the list will also contain the positions of this item.
8311 \see positions, anchor
8314 /* end of documentation of inline functions */
8315 /* start documentation of pure virtual functions */
8317 /*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
8320 Draws this item with the provided \a painter.
8322 The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
8323 function is called. The clipRect depends on the clipping settings defined by \ref
8324 setClipToAxisRect and \ref setClipAxisRect.
8327 /* end documentation of pure virtual functions */
8328 /* start documentation of signals */
8330 /*! \fn void QCPAbstractItem::selectionChanged(bool selected)
8331 This signal is emitted when the selection state of this item has changed, either by user interaction
8332 or by a direct call to \ref setSelected.
8335 /* end documentation of signals */
8338 Base class constructor which initializes base class members.
8340 QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
8341 QCPLayerable(parentPlot),
8342 mClipToAxisRect(false),
8346 QList<QCPAxisRect*> rects = parentPlot->axisRects();
8347 if (rects.size() > 0)
8349 setClipToAxisRect(true);
8350 setClipAxisRect(rects.first());
8354 QCPAbstractItem::~QCPAbstractItem()
8356 // don't delete mPositions because every position is also an anchor and thus in mAnchors
8357 qDeleteAll(mAnchors);
8360 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8361 QCPAxisRect *QCPAbstractItem::clipAxisRect() const
8363 return mClipAxisRect.data();
8367 Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the
8368 entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect.
8370 \see setClipAxisRect
8372 void QCPAbstractItem::setClipToAxisRect(bool clip)
8374 mClipToAxisRect = clip;
8375 if (mClipToAxisRect)
8376 setParentLayerable(mClipAxisRect.data());
8380 Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref
8381 setClipToAxisRect is set to true.
8383 \see setClipToAxisRect
8385 void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)
8387 mClipAxisRect = rect;
8388 if (mClipToAxisRect)
8389 setParentLayerable(mClipAxisRect.data());
8393 Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
8394 (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
8396 However, even when \a selectable was set to false, it is possible to set the selection manually,
8397 by calling \ref setSelected.
8399 \see QCustomPlot::setInteractions, setSelected
8401 void QCPAbstractItem::setSelectable(bool selectable)
8403 if (mSelectable != selectable)
8405 mSelectable = selectable;
8406 emit selectableChanged(mSelectable);
8411 Sets whether this item is selected or not. When selected, it might use a different visual
8412 appearance (e.g. pen and brush), this depends on the specific item though.
8414 The entire selection mechanism for items is handled automatically when \ref
8415 QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this
8416 function when you wish to change the selection state manually.
8418 This function can change the selection state even when \ref setSelectable was set to false.
8420 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
8422 \see setSelectable, selectTest
8424 void QCPAbstractItem::setSelected(bool selected)
8426 if (mSelected != selected)
8428 mSelected = selected;
8429 emit selectionChanged(mSelected);
8434 Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
8435 that name, returns 0.
8437 This function provides an alternative way to access item positions. Normally, you access
8438 positions direcly by their member pointers (which typically have the same variable name as \a
8441 \see positions, anchor
8443 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8445 for (int i=0; i<mPositions.size(); ++i)
8447 if (mPositions.at(i)->name() == name)
8448 return mPositions.at(i);
8450 qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8455 Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
8456 that name, returns 0.
8458 This function provides an alternative way to access item anchors. Normally, you access
8459 anchors direcly by their member pointers (which typically have the same variable name as \a
8462 \see anchors, position
8464 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8466 for (int i=0; i<mAnchors.size(); ++i)
8468 if (mAnchors.at(i)->name() == name)
8469 return mAnchors.at(i);
8471 qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8476 Returns whether this item has an anchor with the specified \a name.
8478 Note that you can check for positions with this function, too. This is because every position is
8479 also an anchor (QCPItemPosition inherits from QCPItemAnchor).
8481 \see anchor, position
8483 bool QCPAbstractItem::hasAnchor(const QString &name) const
8485 for (int i=0; i<mAnchors.size(); ++i)
8487 if (mAnchors.at(i)->name() == name)
8495 Returns the rect the visual representation of this item is clipped to. This depends on the
8496 current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect.
8498 If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
8502 QRect QCPAbstractItem::clipRect() const
8504 if (mClipToAxisRect && mClipAxisRect)
8505 return mClipAxisRect.data()->rect();
8507 return mParentPlot->viewport();
8512 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8513 before drawing item lines.
8515 This is the antialiasing state the painter passed to the \ref draw method is in by default.
8517 This function takes into account the local setting of the antialiasing flag as well as the
8518 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
8519 QCustomPlot::setNotAntialiasedElements.
8523 void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
8525 applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
8530 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
8533 This function may be used to help with the implementation of the \ref selectTest function for
8536 \note This function is identical to QCPAbstractPlottable::distSqrToLine
8540 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8547 double vLengthSqr = v.lengthSquared();
8548 if (!qFuzzyIsNull(vLengthSqr))
8550 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8552 return (a-p).lengthSquared();
8554 return (b-p).lengthSquared();
8556 return ((a + mu*v)-p).lengthSquared();
8558 return (a-p).lengthSquared();
8563 A convenience function which returns the selectTest value for a specified \a rect and a specified
8564 click position \a pos. \a filledRect defines whether a click inside the rect should also be
8565 considered a hit or whether only the rect border is sensitive to hits.
8567 This function may be used to help with the implementation of the \ref selectTest function for
8570 For example, if your item consists of four rects, call this function four times, once for each
8571 rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
8576 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8580 // distance to border:
8581 QList<QLineF> lines;
8582 lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8583 << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8584 double minDistSqr = std::numeric_limits<double>::max();
8585 for (int i=0; i<lines.size(); ++i)
8587 double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8588 if (distSqr < minDistSqr)
8589 minDistSqr = distSqr;
8591 result = qSqrt(minDistSqr);
8593 // filled rect, allow click inside to count as hit:
8594 if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8596 if (rect.contains(pos))
8597 result = mParentPlot->selectionTolerance()*0.99;
8604 Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
8605 item subclasses if they want to provide anchors (QCPItemAnchor).
8607 For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
8608 ids and returns the respective pixel points of the specified anchor.
8612 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8614 qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8620 Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
8621 \a name must be a unique string that is usually identical to the variable name of the position
8622 member (This is needed to provide the name-based \ref position access to positions).
8624 Don't delete positions created by this function manually, as the item will take care of it.
8626 Use this function in the constructor (initialization list) of the specific item subclass to
8627 create each position member. Don't create QCPItemPositions with \b new yourself, because they
8628 won't be registered with the item properly.
8632 QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
8634 if (hasAnchor(name))
8635 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8636 QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8637 mPositions.append(newPosition);
8638 mAnchors.append(newPosition); // every position is also an anchor
8639 newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8640 newPosition->setType(QCPItemPosition::ptPlotCoords);
8641 if (mParentPlot->axisRect())
8642 newPosition->setAxisRect(mParentPlot->axisRect());
8643 newPosition->setCoords(0, 0);
8649 Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
8650 \a name must be a unique string that is usually identical to the variable name of the anchor
8651 member (This is needed to provide the name based \ref anchor access to anchors).
8653 The \a anchorId must be a number identifying the created anchor. It is recommended to create an
8654 enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
8655 to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
8656 the correct pixel coordinates for the passed anchor id.
8658 Don't delete anchors created by this function manually, as the item will take care of it.
8660 Use this function in the constructor (initialization list) of the specific item subclass to
8661 create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
8662 won't be registered with the item properly.
8666 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8668 if (hasAnchor(name))
8669 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8670 QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8671 mAnchors.append(newAnchor);
8675 /* inherits documentation from base class */
8676 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8682 bool selBefore = mSelected;
8683 setSelected(additive ? !mSelected : true);
8684 if (selectionStateChanged)
8685 *selectionStateChanged = mSelected != selBefore;
8689 /* inherits documentation from base class */
8690 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8694 bool selBefore = mSelected;
8696 if (selectionStateChanged)
8697 *selectionStateChanged = mSelected != selBefore;
8701 /* inherits documentation from base class */
8702 QCP::Interaction QCPAbstractItem::selectionCategory() const
8704 return QCP::iSelectItems;
8712 ////////////////////////////////////////////////////////////////////////////////////////////////////
8713 //////////////////// QCustomPlot
8714 ////////////////////////////////////////////////////////////////////////////////////////////////////
8716 /*! \class QCustomPlot
8718 \brief The central class of the library. This is the QWidget which displays the plot and
8719 interacts with the user.
8721 For tutorials on how to use QCustomPlot, see the website\n
8722 http://www.qcustomplot.com/
8725 /* start of documentation of inline functions */
8727 /*! \fn QRect QCustomPlot::viewport() const
8729 Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
8730 drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
8731 plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
8732 (0, 0) and size of the QCustomPlot widget.
8734 Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically
8735 an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger
8736 and contains also the axes themselves, their tick numbers, their labels, the plot title etc.
8738 Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport is temporarily
8739 modified to allow saving plots with sizes independent of the current widget size.
8742 /*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const
8744 Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just
8745 one cell with the main QCPAxisRect inside.
8748 /* end of documentation of inline functions */
8749 /* start of documentation of signals */
8751 /*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
8753 This signal is emitted when the QCustomPlot receives a mouse double click event.
8756 /*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
8758 This signal is emitted when the QCustomPlot receives a mouse press event.
8760 It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8761 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8762 QCPAxisRect::setRangeDragAxes.
8765 /*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
8767 This signal is emitted when the QCustomPlot receives a mouse move event.
8769 It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8770 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8771 QCPAxisRect::setRangeDragAxes.
8773 \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here,
8774 because the dragging starting point was saved the moment the mouse was pressed. Thus it only has
8775 a meaning for the range drag axes that were set at that moment. If you want to change the drag
8776 axes, consider doing this in the \ref mousePress signal instead.
8779 /*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
8781 This signal is emitted when the QCustomPlot receives a mouse release event.
8783 It is emitted before QCustomPlot handles any other mechanisms like object selection. So a
8784 slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or
8785 \ref QCPAbstractPlottable::setSelectable.
8788 /*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
8790 This signal is emitted when the QCustomPlot receives a mouse wheel event.
8792 It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot
8793 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref
8794 QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor.
8797 /*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8799 This signal is emitted when a plottable is clicked.
8801 \a event is the mouse event that caused the click and \a plottable is the plottable that received
8804 \see plottableDoubleClick
8807 /*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8809 This signal is emitted when a plottable is double clicked.
8811 \a event is the mouse event that caused the click and \a plottable is the plottable that received
8817 /*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
8819 This signal is emitted when an item is clicked.
8821 \a event is the mouse event that caused the click and \a item is the item that received the
8824 \see itemDoubleClick
8827 /*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
8829 This signal is emitted when an item is double clicked.
8831 \a event is the mouse event that caused the click and \a item is the item that received the
8837 /*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8839 This signal is emitted when an axis is clicked.
8841 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8842 \a part indicates the part of the axis that was clicked.
8844 \see axisDoubleClick
8847 /*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8849 This signal is emitted when an axis is double clicked.
8851 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8852 \a part indicates the part of the axis that was clicked.
8857 /*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8859 This signal is emitted when a legend (item) is clicked.
8861 \a event is the mouse event that caused the click, \a legend is the legend that received the
8862 click and \a item is the legend item that received the click. If only the legend and no item is
8863 clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8866 \see legendDoubleClick
8869 /*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8871 This signal is emitted when a legend (item) is double clicked.
8873 \a event is the mouse event that caused the click, \a legend is the legend that received the
8874 click and \a item is the legend item that received the click. If only the legend and no item is
8875 clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8881 /*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title)
8883 This signal is emitted when a plot title is clicked.
8885 \a event is the mouse event that caused the click and \a title is the plot title that received
8888 \see titleDoubleClick
8891 /*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
8893 This signal is emitted when a plot title is double clicked.
8895 \a event is the mouse event that caused the click and \a title is the plot title that received
8901 /*! \fn void QCustomPlot::selectionChangedByUser()
8903 This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
8904 clicking. It is not emitted when the selection state of an object has changed programmatically by
8905 a direct call to setSelected() on an object or by calling \ref deselectAll.
8907 In addition to this signal, selectable objects also provide individual signals, for example
8908 QCPAxis::selectionChanged or QCPAbstractPlottable::selectionChanged. Note that those signals are
8909 emitted even if the selection state is changed programmatically.
8911 See the documentation of \ref setInteractions for details about the selection mechanism.
8913 \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends
8916 /*! \fn void QCustomPlot::beforeReplot()
8918 This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
8921 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8922 replot synchronously, it won't cause an infinite recursion.
8924 \see replot, afterReplot
8927 /*! \fn void QCustomPlot::afterReplot()
8929 This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
8932 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8933 replot synchronously, it won't cause an infinite recursion.
8935 \see replot, beforeReplot
8938 /* end of documentation of signals */
8939 /* start of documentation of public members */
8941 /*! \var QCPAxis *QCustomPlot::xAxis
8943 A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
8945 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8946 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8947 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8948 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8949 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8950 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8951 axis rect), the corresponding pointers become 0.
8954 /*! \var QCPAxis *QCustomPlot::yAxis
8956 A pointer to the primary y Axis (left) of the main axis rect of the plot.
8958 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8959 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8960 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8961 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8962 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8963 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8964 axis rect), the corresponding pointers become 0.
8967 /*! \var QCPAxis *QCustomPlot::xAxis2
8969 A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are
8970 invisible by default. Use QCPAxis::setVisible to change this (or use \ref
8971 QCPAxisRect::setupFullAxesBox).
8973 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8974 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8975 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8976 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8977 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8978 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8979 axis rect), the corresponding pointers become 0.
8982 /*! \var QCPAxis *QCustomPlot::yAxis2
8984 A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are
8985 invisible by default. Use QCPAxis::setVisible to change this (or use \ref
8986 QCPAxisRect::setupFullAxesBox).
8988 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8989 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8990 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8991 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8992 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8993 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8994 axis rect), the corresponding pointers become 0.
8997 /*! \var QCPLegend *QCustomPlot::legend
8999 A pointer to the default legend of the main axis rect. The legend is invisible by default. Use
9000 QCPLegend::setVisible to change this.
9002 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
9003 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
9004 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
9005 layout system\endlink to add multiple legends to the plot, use the layout system interface to
9006 access the new legend. For example, legends can be placed inside an axis rect's \ref
9007 QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If
9008 the default legend is removed due to manipulation of the layout system (e.g. by removing the main
9009 axis rect), the corresponding pointer becomes 0.
9012 /* end of documentation of public members */
9015 Constructs a QCustomPlot and sets reasonable default values.
9017 QCustomPlot::QCustomPlot(QWidget *parent) :
9025 mAutoAddPlottableToLegend(true),
9026 mAntialiasedElements(QCP::aeNone),
9027 mNotAntialiasedElements(QCP::aeNone),
9029 mSelectionTolerance(8),
9030 mNoAntialiasingOnDrag(false),
9031 mBackgroundBrush(Qt::white, Qt::SolidPattern),
9032 mBackgroundScaled(true),
9033 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9035 mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9036 mMultiSelectModifier(Qt::ControlModifier),
9037 mPaintBuffer(size()),
9038 mMouseEventElement(0),
9041 setAttribute(Qt::WA_NoMousePropagation);
9042 setAttribute(Qt::WA_OpaquePaintEvent);
9043 setMouseTracking(true);
9044 QLocale currentLocale = locale();
9045 currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9046 setLocale(currentLocale);
9048 // create initial layers:
9049 mLayers.append(new QCPLayer(this, QLatin1String("background")));
9050 mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9051 mLayers.append(new QCPLayer(this, QLatin1String("main")));
9052 mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9053 mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9054 updateLayerIndices();
9055 setCurrentLayer(QLatin1String("main"));
9057 // create initial layout, axis rect and legend:
9058 mPlotLayout = new QCPLayoutGrid;
9059 mPlotLayout->initializeParentPlot(this);
9060 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9061 mPlotLayout->setLayer(QLatin1String("main"));
9062 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9063 mPlotLayout->addElement(0, 0, defaultAxisRect);
9064 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9065 yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9066 xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9067 yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9068 legend = new QCPLegend;
9069 legend->setVisible(false);
9070 defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9071 defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9073 defaultAxisRect->setLayer(QLatin1String("background"));
9074 xAxis->setLayer(QLatin1String("axes"));
9075 yAxis->setLayer(QLatin1String("axes"));
9076 xAxis2->setLayer(QLatin1String("axes"));
9077 yAxis2->setLayer(QLatin1String("axes"));
9078 xAxis->grid()->setLayer(QLatin1String("grid"));
9079 yAxis->grid()->setLayer(QLatin1String("grid"));
9080 xAxis2->grid()->setLayer(QLatin1String("grid"));
9081 yAxis2->grid()->setLayer(QLatin1String("grid"));
9082 legend->setLayer(QLatin1String("legend"));
9084 setViewport(rect()); // needs to be called after mPlotLayout has been created
9089 QCustomPlot::~QCustomPlot()
9101 qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9106 Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement.
9108 This overrides the antialiasing settings for whole element groups, normally controlled with the
9109 \a setAntialiasing function on the individual elements. If an element is neither specified in
9110 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9111 each individual element instance is used.
9113 For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
9114 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9117 if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is
9120 \see setNotAntialiasedElements
9122 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9124 mAntialiasedElements = antialiasedElements;
9126 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9127 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9128 mNotAntialiasedElements |= ~mAntialiasedElements;
9132 Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
9134 See \ref setAntialiasedElements for details.
9136 \see setNotAntialiasedElement
9138 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9140 if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9141 mAntialiasedElements &= ~antialiasedElement;
9142 else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9143 mAntialiasedElements |= antialiasedElement;
9145 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9146 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9147 mNotAntialiasedElements |= ~mAntialiasedElements;
9151 Sets which elements are forcibly drawn not antialiased as an \a or combination of
9152 QCP::AntialiasedElement.
9154 This overrides the antialiasing settings for whole element groups, normally controlled with the
9155 \a setAntialiasing function on the individual elements. If an element is neither specified in
9156 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9157 each individual element instance is used.
9159 For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
9160 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9163 if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
9166 \see setAntialiasedElements
9168 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements)
9170 mNotAntialiasedElements = notAntialiasedElements;
9172 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9173 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9174 mAntialiasedElements |= ~mNotAntialiasedElements;
9178 Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
9180 See \ref setNotAntialiasedElements for details.
9182 \see setAntialiasedElement
9184 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9186 if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9187 mNotAntialiasedElements &= ~notAntialiasedElement;
9188 else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9189 mNotAntialiasedElements |= notAntialiasedElement;
9191 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9192 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9193 mAntialiasedElements |= ~mNotAntialiasedElements;
9197 If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
9198 plottable to the legend (QCustomPlot::legend).
9200 \see addPlottable, addGraph, QCPLegend::addItem
9202 void QCustomPlot::setAutoAddPlottableToLegend(bool on)
9204 mAutoAddPlottableToLegend = on;
9208 Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction
9209 enums. There are the following types of interactions:
9211 <b>Axis range manipulation</b> is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the
9212 respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
9213 For details how to control which axes the user may drag/zoom and in what orientations, see \ref
9214 QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes,
9215 \ref QCPAxisRect::setRangeZoomAxes.
9217 <b>Plottable selection</b> is controlled by \ref QCP::iSelectPlottables. If \ref QCP::iSelectPlottables is
9218 set, the user may select plottables (graphs, curves, bars,...) by clicking on them or in their
9219 vicinity (\ref setSelectionTolerance). Whether the user can actually select a plottable can
9220 further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the specific
9221 plottable. To find out whether a specific plottable is selected, call
9222 QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
9223 \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
9224 function \ref selectedGraphs.
9226 <b>Item selection</b> is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user
9227 may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find
9228 out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of
9229 all currently selected items, call \ref selectedItems.
9231 <b>Axis selection</b> is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user
9232 may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
9233 labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for
9234 each axis. To retrieve a list of all axes that currently contain selected parts, call \ref
9235 selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts().
9237 <b>Legend selection</b> is controlled with \ref QCP::iSelectLegend. If this is set, the user may
9238 select the legend itself or individual items by clicking on them. What parts exactly are
9239 selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the
9240 legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To
9241 find out which child items are selected, call \ref QCPLegend::selectedItems.
9243 <b>All other selectable elements</b> The selection of all other selectable objects (e.g.
9244 QCPPlotTitle, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the
9245 user may select those objects by clicking on them. To find out which are currently selected, you
9246 need to check their selected state explicitly.
9248 If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
9249 emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
9250 their selection state has changed, i.e. not only by user interaction.
9252 To allow multiple objects to be selected by holding the selection modifier (\ref
9253 setMultiSelectModifier), set the flag \ref QCP::iMultiSelect.
9255 \note In addition to the selection mechanism presented here, QCustomPlot always emits
9256 corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
9257 \ref plottableDoubleClick for example.
9259 \see setInteraction, setSelectionTolerance
9261 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9263 mInteractions = interactions;
9267 Sets the single \a interaction of this QCustomPlot to \a enabled.
9269 For details about the interaction system, see \ref setInteractions.
9271 \see setInteractions
9273 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9275 if (!enabled && mInteractions.testFlag(interaction))
9276 mInteractions &= ~interaction;
9277 else if (enabled && !mInteractions.testFlag(interaction))
9278 mInteractions |= interaction;
9282 Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or
9285 If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a
9286 potential selection when the minimum distance between the click position and the graph line is
9287 smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
9288 directly inside the area and ignore this selection tolerance. In other words, it only has meaning
9289 for parts of objects that are too thin to exactly hit with a click and thus need such a
9292 \see setInteractions, QCPLayerable::selectTest
9294 void QCustomPlot::setSelectionTolerance(int pixels)
9296 mSelectionTolerance = pixels;
9300 Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes
9301 ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves
9302 performance during dragging. Thus it creates a more responsive user experience. As soon as the
9303 user stops dragging, the last replot is done with normal antialiasing, to restore high image
9306 \see setAntialiasedElements, setNotAntialiasedElements
9308 void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
9310 mNoAntialiasingOnDrag = enabled;
9314 Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint.
9316 \see setPlottingHint
9318 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9320 mPlottingHints = hints;
9324 Sets the specified plotting \a hint to \a enabled.
9326 \see setPlottingHints
9328 void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
9330 QCP::PlottingHints newHints = mPlottingHints;
9336 if (newHints != mPlottingHints)
9337 setPlottingHints(newHints);
9341 Sets the keyboard modifier that will be recognized as multi-select-modifier.
9343 If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
9344 by clicking on them one after the other while holding down \a modifier.
9346 By default the multi-select-modifier is set to Qt::ControlModifier.
9348 \see setInteractions
9350 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9352 mMultiSelectModifier = modifier;
9356 Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout
9357 (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect.
9359 This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref
9360 savePdf, etc. by temporarily changing the viewport size.
9362 void QCustomPlot::setViewport(const QRect &rect)
9366 mPlotLayout->setOuterRect(mViewport);
9370 Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn
9371 below all other objects in the plot.
9373 For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be
9374 enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is
9375 preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
9376 consider using the overloaded version of this function.
9378 If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will
9379 first be filled with that brush, before drawing the background pixmap. This can be useful for
9380 background pixmaps with translucent areas.
9382 \see setBackgroundScaled, setBackgroundScaledMode
9384 void QCustomPlot::setBackground(const QPixmap &pm)
9386 mBackgroundPixmap = pm;
9387 mScaledBackgroundPixmap = QPixmap();
9391 Sets the background brush of the viewport (see \ref setViewport).
9393 Before drawing everything else, the background is filled with \a brush. If a background pixmap
9394 was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport
9395 before the background pixmap is drawn. This can be useful for background pixmaps with translucent
9398 Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be
9399 useful for exporting to image formats which support transparency, e.g. \ref savePng.
9401 \see setBackgroundScaled, setBackgroundScaledMode
9403 void QCustomPlot::setBackground(const QBrush &brush)
9405 mBackgroundBrush = brush;
9410 Allows setting the background pixmap of the viewport, whether it shall be scaled and how it
9411 shall be scaled in one call.
9413 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
9415 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9417 mBackgroundPixmap = pm;
9418 mScaledBackgroundPixmap = QPixmap();
9419 mBackgroundScaled = scaled;
9420 mBackgroundScaledMode = mode;
9424 Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is
9425 set to true, control whether and how the aspect ratio of the original pixmap is preserved with
9426 \ref setBackgroundScaledMode.
9428 Note that the scaled version of the original pixmap is buffered, so there is no performance
9429 penalty on replots. (Except when the viewport dimensions are changed continuously.)
9431 \see setBackground, setBackgroundScaledMode
9433 void QCustomPlot::setBackgroundScaled(bool scaled)
9435 mBackgroundScaled = scaled;
9439 If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this
9440 function to define whether and how the aspect ratio of the original pixmap is preserved.
9442 \see setBackground, setBackgroundScaled
9444 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9446 mBackgroundScaledMode = mode;
9450 Returns the plottable with \a index. If the index is invalid, returns 0.
9452 There is an overloaded version of this function with no parameter which returns the last added
9453 plottable, see QCustomPlot::plottable()
9455 \see plottableCount, addPlottable
9457 QCPAbstractPlottable *QCustomPlot::plottable(int index)
9459 if (index >= 0 && index < mPlottables.size())
9461 return mPlottables.at(index);
9464 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9471 Returns the last plottable that was added with \ref addPlottable. If there are no plottables in
9472 the plot, returns 0.
9474 \see plottableCount, addPlottable
9476 QCPAbstractPlottable *QCustomPlot::plottable()
9478 if (!mPlottables.isEmpty())
9480 return mPlottables.last();
9486 Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to
9487 the legend (QCustomPlot::legend). QCustomPlot takes ownership of the plottable.
9489 Returns true on success, i.e. when \a plottable isn't already in the plot and the parent plot of
9490 \a plottable is this QCustomPlot (the latter is controlled by what axes were passed in the
9491 plottable's constructor).
9493 \see plottable, plottableCount, removePlottable, clearPlottables
9495 bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
9497 if (mPlottables.contains(plottable))
9499 qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9502 if (plottable->parentPlot() != this)
9504 qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9508 mPlottables.append(plottable);
9509 // possibly add plottable to legend:
9510 if (mAutoAddPlottableToLegend)
9511 plottable->addToLegend();
9512 // special handling for QCPGraphs to maintain the simple graph interface:
9513 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9514 mGraphs.append(graph);
9515 if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9516 plottable->setLayer(currentLayer());
9521 Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend).
9523 Returns true on success.
9525 \see addPlottable, clearPlottables
9527 bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
9529 if (!mPlottables.contains(plottable))
9531 qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9535 // remove plottable from legend:
9536 plottable->removeFromLegend();
9537 // special handling for QCPGraphs to maintain the simple graph interface:
9538 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9539 mGraphs.removeOne(graph);
9540 // remove plottable:
9542 mPlottables.removeOne(plottable);
9548 Removes the plottable by its \a index.
9550 bool QCustomPlot::removePlottable(int index)
9552 if (index >= 0 && index < mPlottables.size())
9553 return removePlottable(mPlottables[index]);
9556 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9562 Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
9564 Returns the number of plottables removed.
9566 \see removePlottable
9568 int QCustomPlot::clearPlottables()
9570 int c = mPlottables.size();
9571 for (int i=c-1; i >= 0; --i)
9572 removePlottable(mPlottables[i]);
9577 Returns the number of currently existing plottables in the plot
9579 \see plottable, addPlottable
9581 int QCustomPlot::plottableCount() const
9583 return mPlottables.size();
9587 Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
9589 There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
9591 \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9593 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9595 QList<QCPAbstractPlottable*> result;
9596 foreach (QCPAbstractPlottable *plottable, mPlottables)
9598 if (plottable->selected())
9599 result.append(plottable);
9605 Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
9606 (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple
9607 plottables come into consideration, the one closest to \a pos is returned.
9609 If \a onlySelectable is true, only plottables that are selectable
9610 (QCPAbstractPlottable::setSelectable) are considered.
9612 If there is no plottable at \a pos, the return value is 0.
9614 \see itemAt, layoutElementAt
9616 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9618 QCPAbstractPlottable *resultPlottable = 0;
9619 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9621 foreach (QCPAbstractPlottable *plottable, mPlottables)
9623 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
9625 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
9627 double currentDistance = plottable->selectTest(pos, false);
9628 if (currentDistance >= 0 && currentDistance < resultDistance)
9630 resultPlottable = plottable;
9631 resultDistance = currentDistance;
9636 return resultPlottable;
9640 Returns whether this QCustomPlot instance contains the \a plottable.
9644 bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
9646 return mPlottables.contains(plottable);
9650 Returns the graph with \a index. If the index is invalid, returns 0.
9652 There is an overloaded version of this function with no parameter which returns the last created
9653 graph, see QCustomPlot::graph()
9655 \see graphCount, addGraph
9657 QCPGraph *QCustomPlot::graph(int index) const
9659 if (index >= 0 && index < mGraphs.size())
9661 return mGraphs.at(index);
9664 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9671 Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
9674 \see graphCount, addGraph
9676 QCPGraph *QCustomPlot::graph() const
9678 if (!mGraphs.isEmpty())
9680 return mGraphs.last();
9686 Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
9687 bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a
9688 keyAxis and \a valueAxis must reside in this QCustomPlot.
9690 \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
9693 Returns a pointer to the newly created graph, or 0 if adding the graph failed.
9695 \see graph, graphCount, removeGraph, clearGraphs
9697 QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
9699 if (!keyAxis) keyAxis = xAxis;
9700 if (!valueAxis) valueAxis = yAxis;
9701 if (!keyAxis || !valueAxis)
9703 qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9706 if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9708 qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9712 QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9713 if (addPlottable(newGraph))
9715 newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9725 Removes the specified \a graph from the plot and, if necessary, from the QCustomPlot::legend. If
9726 any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
9727 property of those graphs is reset to zero (no channel fill).
9729 Returns true on success.
9733 bool QCustomPlot::removeGraph(QCPGraph *graph)
9735 return removePlottable(graph);
9740 Removes the graph by its \a index.
9742 bool QCustomPlot::removeGraph(int index)
9744 if (index >= 0 && index < mGraphs.size())
9745 return removeGraph(mGraphs[index]);
9751 Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
9753 Returns the number of graphs removed.
9757 int QCustomPlot::clearGraphs()
9759 int c = mGraphs.size();
9760 for (int i=c-1; i >= 0; --i)
9761 removeGraph(mGraphs[i]);
9766 Returns the number of currently existing graphs in the plot
9768 \see graph, addGraph
9770 int QCustomPlot::graphCount() const
9772 return mGraphs.size();
9776 Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
9778 If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars,
9779 etc., use \ref selectedPlottables.
9781 \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9783 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9785 QList<QCPGraph*> result;
9786 foreach (QCPGraph *graph, mGraphs)
9788 if (graph->selected())
9789 result.append(graph);
9795 Returns the item with \a index. If the index is invalid, returns 0.
9797 There is an overloaded version of this function with no parameter which returns the last added
9798 item, see QCustomPlot::item()
9800 \see itemCount, addItem
9802 QCPAbstractItem *QCustomPlot::item(int index) const
9804 if (index >= 0 && index < mItems.size())
9806 return mItems.at(index);
9809 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9816 Returns the last item, that was added with \ref addItem. If there are no items in the plot,
9819 \see itemCount, addItem
9821 QCPAbstractItem *QCustomPlot::item() const
9823 if (!mItems.isEmpty())
9825 return mItems.last();
9831 Adds the specified item to the plot. QCustomPlot takes ownership of the item.
9833 Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a
9834 item is this QCustomPlot.
9836 \see item, itemCount, removeItem, clearItems
9838 bool QCustomPlot::addItem(QCPAbstractItem *item)
9840 if (!mItems.contains(item) && item->parentPlot() == this)
9842 mItems.append(item);
9846 qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9852 Removes the specified item from the plot.
9854 Returns true on success.
9856 \see addItem, clearItems
9858 bool QCustomPlot::removeItem(QCPAbstractItem *item)
9860 if (mItems.contains(item))
9863 mItems.removeOne(item);
9867 qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9874 Removes the item by its \a index.
9876 bool QCustomPlot::removeItem(int index)
9878 if (index >= 0 && index < mItems.size())
9879 return removeItem(mItems[index]);
9882 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9888 Removes all items from the plot.
9890 Returns the number of items removed.
9894 int QCustomPlot::clearItems()
9896 int c = mItems.size();
9897 for (int i=c-1; i >= 0; --i)
9898 removeItem(mItems[i]);
9903 Returns the number of currently existing items in the plot
9907 int QCustomPlot::itemCount() const
9909 return mItems.size();
9913 Returns a list of the selected items. If no items are currently selected, the list is empty.
9915 \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
9917 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9919 QList<QCPAbstractItem*> result;
9920 foreach (QCPAbstractItem *item, mItems)
9922 if (item->selected())
9923 result.append(item);
9929 Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
9930 QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
9931 setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
9934 If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
9937 If there is no item at \a pos, the return value is 0.
9939 \see plottableAt, layoutElementAt
9941 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9943 QCPAbstractItem *resultItem = 0;
9944 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9946 foreach (QCPAbstractItem *item, mItems)
9948 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
9950 if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9952 double currentDistance = item->selectTest(pos, false);
9953 if (currentDistance >= 0 && currentDistance < resultDistance)
9956 resultDistance = currentDistance;
9965 Returns whether this QCustomPlot contains the \a item.
9969 bool QCustomPlot::hasItem(QCPAbstractItem *item) const
9971 return mItems.contains(item);
9975 Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is
9978 Layer names are case-sensitive.
9980 \see addLayer, moveLayer, removeLayer
9982 QCPLayer *QCustomPlot::layer(const QString &name) const
9984 foreach (QCPLayer *layer, mLayers)
9986 if (layer->name() == name)
9994 Returns the layer by \a index. If the index is invalid, 0 is returned.
9996 \see addLayer, moveLayer, removeLayer
9998 QCPLayer *QCustomPlot::layer(int index) const
10000 if (index >= 0 && index < mLayers.size())
10002 return mLayers.at(index);
10005 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10011 Returns the layer that is set as current layer (see \ref setCurrentLayer).
10013 QCPLayer *QCustomPlot::currentLayer() const
10015 return mCurrentLayer;
10019 Sets the layer with the specified \a name to be the current layer. All layerables (\ref
10020 QCPLayerable), e.g. plottables and items, are created on the current layer.
10022 Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
10024 Layer names are case-sensitive.
10026 \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer
10028 bool QCustomPlot::setCurrentLayer(const QString &name)
10030 if (QCPLayer *newCurrentLayer = layer(name))
10032 return setCurrentLayer(newCurrentLayer);
10035 qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10042 Sets the provided \a layer to be the current layer.
10044 Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
10046 \see addLayer, moveLayer, removeLayer
10048 bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
10050 if (!mLayers.contains(layer))
10052 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10056 mCurrentLayer = layer;
10061 Returns the number of currently existing layers in the plot
10063 \see layer, addLayer
10065 int QCustomPlot::layerCount() const
10067 return mLayers.size();
10071 Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which
10072 must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer.
10074 Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
10075 valid layer inside this QCustomPlot.
10077 If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
10079 For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
10081 \see layer, moveLayer, removeLayer
10083 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10086 otherLayer = mLayers.last();
10087 if (!mLayers.contains(otherLayer))
10089 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10094 qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10098 QCPLayer *newLayer = new QCPLayer(this, name);
10099 mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10100 updateLayerIndices();
10105 Removes the specified \a layer and returns true on success.
10107 All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
10108 \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
10109 cases, the total rendering order of all layerables in the QCustomPlot is preserved.
10111 If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
10112 layer) becomes the new current layer.
10114 It is not possible to remove the last layer of the plot.
10116 \see layer, addLayer, moveLayer
10118 bool QCustomPlot::removeLayer(QCPLayer *layer)
10120 if (!mLayers.contains(layer))
10122 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10125 if (mLayers.size() < 2)
10127 qDebug() << Q_FUNC_INFO << "can't remove last layer";
10131 // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10132 int removedIndex = layer->index();
10133 bool isFirstLayer = removedIndex==0;
10134 QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10135 QList<QCPLayerable*> children = layer->children();
10136 if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10138 for (int i=children.size()-1; i>=0; --i)
10139 children.at(i)->moveToLayer(targetLayer, true);
10140 } else // append normally
10142 for (int i=0; i<children.size(); ++i)
10143 children.at(i)->moveToLayer(targetLayer, false);
10145 // if removed layer is current layer, change current layer to layer below/above:
10146 if (layer == mCurrentLayer)
10147 setCurrentLayer(targetLayer);
10150 mLayers.removeOne(layer);
10151 updateLayerIndices();
10156 Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or
10157 below is controlled with \a insertMode.
10159 Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
10162 \see layer, addLayer, moveLayer
10164 bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10166 if (!mLayers.contains(layer))
10168 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10171 if (!mLayers.contains(otherLayer))
10173 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10177 if (layer->index() > otherLayer->index())
10178 mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10179 else if (layer->index() < otherLayer->index())
10180 mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
10182 updateLayerIndices();
10187 Returns the number of axis rects in the plot.
10189 All axis rects can be accessed via QCustomPlot::axisRect().
10191 Initially, only one axis rect exists in the plot.
10193 \see axisRect, axisRects
10195 int QCustomPlot::axisRectCount() const
10197 return axisRects().size();
10201 Returns the axis rect with \a index.
10203 Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were
10204 added, all of them may be accessed with this function in a linear fashion (even when they are
10205 nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).
10207 \see axisRectCount, axisRects
10209 QCPAxisRect *QCustomPlot::axisRect(int index) const
10211 const QList<QCPAxisRect*> rectList = axisRects();
10212 if (index >= 0 && index < rectList.size())
10214 return rectList.at(index);
10217 qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10223 Returns all axis rects in the plot.
10225 \see axisRectCount, axisRect
10227 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10229 QList<QCPAxisRect*> result;
10230 QStack<QCPLayoutElement*> elementStack;
10232 elementStack.push(mPlotLayout);
10234 while (!elementStack.isEmpty())
10236 foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10240 elementStack.push(element);
10241 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10251 Returns the layout element at pixel position \a pos. If there is no element at that position,
10254 Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on
10255 any of its parent elements is set to false, it will not be considered.
10257 \see itemAt, plottableAt
10259 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10261 QCPLayoutElement *currentElement = mPlotLayout;
10262 bool searchSubElements = true;
10263 while (searchSubElements && currentElement)
10265 searchSubElements = false;
10266 foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10268 if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10270 currentElement = subElement;
10271 searchSubElements = true;
10276 return currentElement;
10280 Returns the axes that currently have selected parts, i.e. whose selection state is not \ref
10283 \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts,
10284 QCPAxis::setSelectableParts
10286 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10288 QList<QCPAxis*> result, allAxes;
10289 foreach (QCPAxisRect *rect, axisRects())
10290 allAxes << rect->axes();
10292 foreach (QCPAxis *axis, allAxes)
10294 if (axis->selectedParts() != QCPAxis::spNone)
10295 result.append(axis);
10302 Returns the legends that currently have selected parts, i.e. whose selection state is not \ref
10305 \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts,
10306 QCPLegend::setSelectableParts, QCPLegend::selectedItems
10308 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10310 QList<QCPLegend*> result;
10312 QStack<QCPLayoutElement*> elementStack;
10314 elementStack.push(mPlotLayout);
10316 while (!elementStack.isEmpty())
10318 foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10322 elementStack.push(subElement);
10323 if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10325 if (leg->selectedParts() != QCPLegend::spNone)
10326 result.append(leg);
10336 Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
10338 Since calling this function is not a user interaction, this does not emit the \ref
10339 selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
10340 objects were previously selected.
10342 \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
10344 void QCustomPlot::deselectAll()
10346 foreach (QCPLayer *layer, mLayers)
10348 foreach (QCPLayerable *layerable, layer->children())
10349 layerable->deselectEvent(0);
10354 Causes a complete replot into the internal buffer. Finally, update() is called, to redraw the
10355 buffer on the QCustomPlot widget surface. This is the method that must be called to make changes,
10356 for example on the axis ranges or data points of graphs, visible.
10358 Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the
10359 QCustomPlot widget and user interactions (object selection and range dragging/zooming).
10361 Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref
10362 afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two
10363 signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10366 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
10368 if (mReplotting) // incase signals loop back to replot slot
10370 mReplotting = true;
10371 emit beforeReplot();
10373 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10374 QCPPainter painter;
10375 painter.begin(&mPaintBuffer);
10376 if (painter.isActive())
10378 painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10379 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10380 painter.fillRect(mViewport, mBackgroundBrush);
10383 if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10387 } else // might happen if QCustomPlot has width or height zero
10388 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10390 emit afterReplot();
10391 mReplotting = false;
10395 Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
10397 if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true
10398 (QCPLayerable::setVisible), will be used to rescale the axes.
10400 \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale
10402 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10404 QList<QCPAxis*> allAxes;
10405 foreach (QCPAxisRect *rect, axisRects())
10406 allAxes << rect->axes();
10408 foreach (QCPAxis *axis, allAxes)
10409 axis->rescale(onlyVisiblePlottables);
10413 Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
10414 of texts and lines will be derived from the specified \a width and \a height. This means, the
10415 output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
10416 pixel width and height. If either \a width or \a height is zero, the exported image will have the
10417 same dimensions as the QCustomPlot widget currently has.
10419 \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
10420 are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
10421 zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see the QPainter
10422 and QPen documentation.
10424 The objects of the plot will appear in the current selection state. If you don't want any
10425 selected objects to be painted in their selected look, deselect everything with \ref deselectAll
10426 before calling this function.
10428 Returns true on success.
10431 \li If you plan on editing the exported PDF file with a vector graphics editor like
10432 Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
10433 (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
10434 \li If calling this function inside the constructor of the parent of the QCustomPlot widget
10435 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10436 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10437 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10438 aren't defined yet inside the constructor, so you would get an image that has strange
10441 \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
10444 \note On Android systems, this method does nothing and issues an according qDebug warning
10445 message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10447 \see savePng, saveBmp, saveJpg, saveRastered
10449 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10451 bool success = false;
10452 #ifdef QT_NO_PRINTER
10454 Q_UNUSED(noCosmeticPen)
10457 Q_UNUSED(pdfCreator)
10459 qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10461 int newWidth, newHeight;
10462 if (width == 0 || height == 0)
10464 newWidth = this->width();
10465 newHeight = this->height();
10469 newHeight = height;
10472 QPrinter printer(QPrinter::ScreenResolution);
10473 printer.setOutputFileName(fileName);
10474 printer.setOutputFormat(QPrinter::PdfFormat);
10475 printer.setColorMode(QPrinter::Color);
10476 printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10477 printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10478 QRect oldViewport = viewport();
10479 setViewport(QRect(0, 0, newWidth, newHeight));
10480 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10481 printer.setFullPage(true);
10482 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10484 QPageLayout pageLayout;
10485 pageLayout.setMode(QPageLayout::FullPageMode);
10486 pageLayout.setOrientation(QPageLayout::Portrait);
10487 pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10488 pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10489 printer.setPageLayout(pageLayout);
10491 QCPPainter printpainter;
10492 if (printpainter.begin(&printer))
10494 printpainter.setMode(QCPPainter::pmVectorized);
10495 printpainter.setMode(QCPPainter::pmNoCaching);
10496 printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10497 printpainter.setWindow(mViewport);
10498 if (mBackgroundBrush.style() != Qt::NoBrush &&
10499 mBackgroundBrush.color() != Qt::white &&
10500 mBackgroundBrush.color() != Qt::transparent &&
10501 mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10502 printpainter.fillRect(viewport(), mBackgroundBrush);
10503 draw(&printpainter);
10504 printpainter.end();
10507 setViewport(oldViewport);
10508 #endif // QT_NO_PRINTER
10513 Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
10514 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10515 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10516 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10518 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10519 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10520 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10521 200*200 pixel resolution.
10523 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10524 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10525 QCustomPlot to place objects with sub-pixel accuracy.
10527 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10528 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10529 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10530 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10531 aren't defined yet inside the constructor, so you would get an image that has strange
10534 The objects of the plot will appear in the current selection state. If you don't want any selected
10535 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10538 If you want the PNG to have a transparent background, call \ref setBackground(const QBrush
10539 &brush) with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving.
10541 PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10542 -1 to use the default setting.
10544 Returns true on success. If this function fails, most likely the PNG format isn't supported by
10545 the system, see Qt docs about QImageWriter::supportedImageFormats().
10547 \see savePdf, saveBmp, saveJpg, saveRastered
10549 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10551 return saveRastered(fileName, width, height, scale, "PNG", quality);
10555 Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
10556 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10557 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10558 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10560 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10561 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10562 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10563 200*200 pixel resolution.
10565 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10566 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10567 QCustomPlot to place objects with sub-pixel accuracy.
10569 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10570 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10571 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10572 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10573 aren't defined yet inside the constructor, so you would get an image that has strange
10576 The objects of the plot will appear in the current selection state. If you don't want any selected
10577 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10580 JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10581 -1 to use the default setting.
10583 Returns true on success. If this function fails, most likely the JPG format isn't supported by
10584 the system, see Qt docs about QImageWriter::supportedImageFormats().
10586 \see savePdf, savePng, saveBmp, saveRastered
10588 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10590 return saveRastered(fileName, width, height, scale, "JPG", quality);
10594 Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
10595 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10596 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10597 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10599 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10600 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10601 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10602 200*200 pixel resolution.
10604 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10605 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10606 QCustomPlot to place objects with sub-pixel accuracy.
10608 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10609 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10610 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10611 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10612 aren't defined yet inside the constructor, so you would get an image that has strange
10615 The objects of the plot will appear in the current selection state. If you don't want any selected
10616 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10619 Returns true on success. If this function fails, most likely the BMP format isn't supported by
10620 the system, see Qt docs about QImageWriter::supportedImageFormats().
10622 \see savePdf, savePng, saveJpg, saveRastered
10624 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10626 return saveRastered(fileName, width, height, scale, "BMP");
10631 Returns a minimum size hint that corresponds to the minimum size of the top level layout
10632 (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum
10633 size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot.
10634 This is especially important, when placed in a QLayout where other components try to take in as
10635 much space as possible (e.g. QMdiArea).
10637 QSize QCustomPlot::minimumSizeHint() const
10639 return mPlotLayout->minimumSizeHint();
10644 Returns a size hint that is the same as \ref minimumSizeHint.
10647 QSize QCustomPlot::sizeHint() const
10649 return mPlotLayout->minimumSizeHint();
10654 Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but
10655 draws the internal buffer on the widget surface.
10657 void QCustomPlot::paintEvent(QPaintEvent *event)
10660 QPainter painter(this);
10661 painter.drawPixmap(0, 0, mPaintBuffer);
10666 Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
10667 the new size. The viewport (which becomes the outer rect of mPlotLayout) is resized
10668 appropriately. Finally a \ref replot is performed.
10670 void QCustomPlot::resizeEvent(QResizeEvent *event)
10672 // resize and repaint the buffer:
10673 mPaintBuffer = QPixmap(event->size());
10674 setViewport(rect());
10675 replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10680 Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then emits
10681 the specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref
10682 axisDoubleClick, etc.). Finally determines the affected layout element and forwards the event to
10685 \see mousePressEvent, mouseReleaseEvent
10687 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10689 emit mouseDoubleClick(event);
10692 QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10694 // emit specialized object double click signals:
10695 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10696 emit plottableDoubleClick(ap, event);
10697 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10698 emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10699 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10700 emit itemDoubleClick(ai, event);
10701 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10702 emit legendDoubleClick(lg, 0, event);
10703 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10704 emit legendDoubleClick(li->parentLegend(), li, event);
10705 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10706 emit titleDoubleClick(event, pt);
10708 // call double click event of affected layout element:
10709 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10710 el->mouseDoubleClickEvent(event);
10712 // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10713 if (mMouseEventElement)
10715 mMouseEventElement->mouseReleaseEvent(event);
10716 mMouseEventElement = 0;
10719 //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10724 Event handler for when a mouse button is pressed. Emits the mousePress signal. Then determines
10725 the affected layout element and forwards the event to it.
10727 \see mouseMoveEvent, mouseReleaseEvent
10729 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10731 emit mousePress(event);
10732 mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10734 // call event of affected layout element:
10735 mMouseEventElement = layoutElementAt(event->pos());
10736 if (mMouseEventElement)
10737 mMouseEventElement->mousePressEvent(event);
10739 QWidget::mousePressEvent(event);
10744 Event handler for when the cursor is moved. Emits the \ref mouseMove signal.
10746 If a layout element has mouse capture focus (a mousePressEvent happened on top of the layout
10747 element before), the mouseMoveEvent is forwarded to that element.
10749 \see mousePressEvent, mouseReleaseEvent
10751 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10753 emit mouseMove(event);
10755 // call event of affected layout element:
10756 if (mMouseEventElement)
10757 mMouseEventElement->mouseMoveEvent(event);
10759 QWidget::mouseMoveEvent(event);
10764 Event handler for when a mouse button is released. Emits the \ref mouseRelease signal.
10766 If the mouse was moved less than a certain threshold in any direction since the \ref
10767 mousePressEvent, it is considered a click which causes the selection mechanism (if activated via
10768 \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse
10769 click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.)
10771 If a layout element has mouse capture focus (a \ref mousePressEvent happened on top of the layout
10772 element before), the \ref mouseReleaseEvent is forwarded to that element.
10774 \see mousePressEvent, mouseMoveEvent
10776 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10778 emit mouseRelease(event);
10779 bool doReplot = false;
10781 if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10783 if (event->button() == Qt::LeftButton)
10785 // handle selection mechanism:
10787 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10788 bool selectionStateChanged = false;
10789 bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10790 // deselect all other layerables if not additive selection:
10793 foreach (QCPLayer *layer, mLayers)
10795 foreach (QCPLayerable *layerable, layer->children())
10797 if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10799 bool selChanged = false;
10800 layerable->deselectEvent(&selChanged);
10801 selectionStateChanged |= selChanged;
10806 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10808 // a layerable was actually clicked, call its selectEvent:
10809 bool selChanged = false;
10810 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10811 selectionStateChanged |= selChanged;
10813 if (selectionStateChanged)
10816 emit selectionChangedByUser();
10820 // emit specialized object click signals:
10822 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
10823 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10824 emit plottableClick(ap, event);
10825 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10826 emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10827 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10828 emit itemClick(ai, event);
10829 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10830 emit legendClick(lg, 0, event);
10831 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10832 emit legendClick(li->parentLegend(), li, event);
10833 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10834 emit titleClick(event, pt);
10837 // call event of affected layout element:
10838 if (mMouseEventElement)
10840 mMouseEventElement->mouseReleaseEvent(event);
10841 mMouseEventElement = 0;
10844 if (doReplot || noAntialiasingOnDrag())
10847 QWidget::mouseReleaseEvent(event);
10852 Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then
10853 determines the affected layout element and forwards the event to it.
10856 void QCustomPlot::wheelEvent(QWheelEvent *event)
10858 emit mouseWheel(event);
10860 // call event of affected layout element:
10861 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10862 el->wheelEvent(event);
10864 QWidget::wheelEvent(event);
10869 This is the main draw function. It draws the entire plot, including background pixmap, with the
10870 specified \a painter. Note that it does not fill the background with the background brush (as the
10871 user may specify with \ref setBackground(const QBrush &brush)), this is up to the respective
10872 functions calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter).
10874 void QCustomPlot::draw(QCPPainter *painter)
10876 // run through layout phases:
10877 mPlotLayout->update(QCPLayoutElement::upPreparation);
10878 mPlotLayout->update(QCPLayoutElement::upMargins);
10879 mPlotLayout->update(QCPLayoutElement::upLayout);
10881 // draw viewport background pixmap:
10882 drawBackground(painter);
10884 // draw all layered objects (grid, axes, plottables, items, legend,...):
10885 foreach (QCPLayer *layer, mLayers)
10887 foreach (QCPLayerable *child, layer->children())
10889 if (child->realVisibility())
10892 painter->setClipRect(child->clipRect().translated(0, -1));
10893 child->applyDefaultAntialiasingHint(painter);
10894 child->draw(painter);
10895 painter->restore();
10900 /* Debug code to draw all layout element rects
10901 foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10903 painter->setBrush(Qt::NoBrush);
10904 painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10905 painter->drawRect(el->rect());
10906 painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10907 painter->drawRect(el->outerRect());
10914 Draws the viewport background pixmap of the plot.
10916 If a pixmap was provided via \ref setBackground, this function buffers the scaled version
10917 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
10918 the viewport with the provided \a painter. The scaled version is buffered in
10919 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10920 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
10921 dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
10924 Note that this function does not draw a fill with the background brush (\ref setBackground(const
10925 QBrush &brush)) beneath the pixmap.
10927 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
10929 void QCustomPlot::drawBackground(QCPPainter *painter)
10931 // Note: background color is handled in individual replot/save functions
10933 // draw background pixmap (on top of fill, if brush specified):
10934 if (!mBackgroundPixmap.isNull())
10936 if (mBackgroundScaled)
10938 // check whether mScaledBackground needs to be updated:
10939 QSize scaledSize(mBackgroundPixmap.size());
10940 scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10941 if (mScaledBackgroundPixmap.size() != scaledSize)
10942 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10943 painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10946 painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10954 This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot
10955 so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly.
10957 void QCustomPlot::axisRemoved(QCPAxis *axis)
10961 if (xAxis2 == axis)
10965 if (yAxis2 == axis)
10968 // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
10973 This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so
10974 it may clear its QCustomPlot::legend member accordingly.
10976 void QCustomPlot::legendRemoved(QCPLegend *legend)
10978 if (this->legend == legend)
10984 Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called
10985 after every operation that changes the layer indices, like layer removal, layer creation, layer
10988 void QCustomPlot::updateLayerIndices() const
10990 for (int i=0; i<mLayers.size(); ++i)
10991 mLayers.at(i)->mIndex = i;
10996 Returns the layerable at pixel position \a pos. If \a onlySelectable is set to true, only those
10997 layerables that are selectable will be considered. (Layerable subclasses communicate their
10998 selectability via the QCPLayerable::selectTest method, by returning -1.)
11000 \a selectionDetails is an output parameter that contains selection specifics of the affected
11001 layerable. This is useful if the respective layerable shall be given a subsequent
11002 QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains
11003 information about which part of the layerable was hit, in multi-part layerables (e.g.
11004 QCPAxis::SelectablePart).
11006 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11008 for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11010 const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11011 double minimumDistance = selectionTolerance()*1.1;
11012 QCPLayerable *minimumDistanceLayerable = 0;
11013 for (int i=layerables.size()-1; i>=0; --i)
11015 if (!layerables.at(i)->realVisibility())
11018 double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11019 if (dist >= 0 && dist < minimumDistance)
11021 minimumDistance = dist;
11022 minimumDistanceLayerable = layerables.at(i);
11023 if (selectionDetails) *selectionDetails = details;
11026 if (minimumDistance < selectionTolerance())
11027 return minimumDistanceLayerable;
11033 Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is
11034 sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead
11035 to a full resolution file with width 200.) If the \a format supports compression, \a quality may
11036 be between 0 and 100 to control it.
11038 Returns true on success. If this function fails, most likely the given \a format isn't supported
11039 by the system, see Qt docs about QImageWriter::supportedImageFormats().
11041 \see saveBmp, saveJpg, savePng, savePdf
11043 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11045 QPixmap buffer = toPixmap(width, height, scale);
11046 if (!buffer.isNull())
11047 return buffer.save(fileName, format, quality);
11053 Renders the plot to a pixmap and returns it.
11055 The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and
11056 scale 2.0 lead to a full resolution pixmap with width 200.)
11058 \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf
11060 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11062 // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11063 int newWidth, newHeight;
11064 if (width == 0 || height == 0)
11066 newWidth = this->width();
11067 newHeight = this->height();
11071 newHeight = height;
11073 int scaledWidth = qRound(scale*newWidth);
11074 int scaledHeight = qRound(scale*newHeight);
11076 QPixmap result(scaledWidth, scaledHeight);
11077 result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11078 QCPPainter painter;
11079 painter.begin(&result);
11080 if (painter.isActive())
11082 QRect oldViewport = viewport();
11083 setViewport(QRect(0, 0, newWidth, newHeight));
11084 painter.setMode(QCPPainter::pmNoCaching);
11085 if (!qFuzzyCompare(scale, 1.0))
11087 if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11088 painter.setMode(QCPPainter::pmNonCosmetic);
11089 painter.scale(scale, scale);
11091 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
11092 painter.fillRect(mViewport, mBackgroundBrush);
11094 setViewport(oldViewport);
11096 } else // might happen if pixmap has width or height zero
11098 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11105 Renders the plot using the passed \a painter.
11107 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
11108 appear scaled accordingly.
11110 \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter
11111 on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with
11112 the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter.
11116 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11118 // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11119 int newWidth, newHeight;
11120 if (width == 0 || height == 0)
11122 newWidth = this->width();
11123 newHeight = this->height();
11127 newHeight = height;
11130 if (painter->isActive())
11132 QRect oldViewport = viewport();
11133 setViewport(QRect(0, 0, newWidth, newHeight));
11134 painter->setMode(QCPPainter::pmNoCaching);
11135 if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
11136 painter->fillRect(mViewport, mBackgroundBrush);
11138 setViewport(oldViewport);
11140 qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11144 ////////////////////////////////////////////////////////////////////////////////////////////////////
11145 //////////////////// QCPColorGradient
11146 ////////////////////////////////////////////////////////////////////////////////////////////////////
11148 /*! \class QCPColorGradient
11149 \brief Defines a color gradient for use with e.g. \ref QCPColorMap
11151 This class describes a color gradient which can be used to encode data with color. For example,
11152 QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which
11153 take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
11154 with a \a position from 0 to 1. In between these defined color positions, the
11155 color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
11157 Alternatively, load one of the preset color gradients shown in the image below, with \ref
11158 loadPreset, or by directly specifying the preset in the constructor.
11160 \image html QCPColorGradient.png
11162 The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
11163 converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
11164 GradientPreset to all the \a setGradient methods, e.g.:
11165 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient
11167 The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
11168 color gradient shall be applied periodically (wrapping around) to data values that lie outside
11169 the data range specified on the plottable instance can be controlled with \ref setPeriodic.
11173 Constructs a new QCPColorGradient initialized with the colors and color interpolation according
11176 The color level count is initialized to 350.
11178 QCPColorGradient::QCPColorGradient(GradientPreset preset) :
11180 mColorInterpolation(ciRGB),
11182 mColorBufferInvalidated(true)
11184 mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11185 loadPreset(preset);
11188 /* undocumented operator */
11189 bool QCPColorGradient::operator==(const QCPColorGradient &other) const
11191 return ((other.mLevelCount == this->mLevelCount) &&
11192 (other.mColorInterpolation == this->mColorInterpolation) &&
11193 (other.mPeriodic == this->mPeriodic) &&
11194 (other.mColorStops == this->mColorStops));
11198 Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
11199 is typically enough to create a smooth appearance.
11201 \image html QCPColorGradient-levelcount.png
11203 void QCPColorGradient::setLevelCount(int n)
11207 qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11210 if (n != mLevelCount)
11213 mColorBufferInvalidated = true;
11218 Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
11219 colors are the values of the passed QMap \a colorStops. In between these color stops, the color
11220 is interpolated according to \ref setColorInterpolation.
11222 A more convenient way to create a custom gradient may be to clear all color stops with \ref
11223 clearColorStops and then adding them one by one with \ref setColorStopAt.
11225 \see clearColorStops
11227 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11229 mColorStops = colorStops;
11230 mColorBufferInvalidated = true;
11234 Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
11235 these color stops, the color is interpolated according to \ref setColorInterpolation.
11237 \see setColorStops, clearColorStops
11239 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11241 mColorStops.insert(position, color);
11242 mColorBufferInvalidated = true;
11246 Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
11247 interpolated linearly in RGB or in HSV color space.
11249 For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
11250 whereas in HSV space the intermediate color is yellow.
11252 void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
11254 if (interpolation != mColorInterpolation)
11256 mColorInterpolation = interpolation;
11257 mColorBufferInvalidated = true;
11262 Sets whether data points that are outside the configured data range (e.g. \ref
11263 QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
11264 they all have the same color, corresponding to the respective gradient boundary color.
11266 \image html QCPColorGradient-periodic.png
11268 As shown in the image above, gradients that have the same start and end color are especially
11269 suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
11270 the color map. A preset that has this property is \ref gpHues.
11272 In practice, using periodic color gradients makes sense when the data corresponds to a periodic
11273 dimension, such as an angle or a phase. If this is not the case, the color encoding might become
11274 ambiguous, because multiple different data values are shown as the same color.
11276 void QCPColorGradient::setPeriodic(bool enabled)
11278 mPeriodic = enabled;
11282 This method is used to quickly convert a \a data array to colors. The colors will be output in
11283 the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
11284 function. The data range that shall be used for mapping the data value to the gradient is passed
11285 in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
11288 if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
11289 set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
11290 array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
11291 is addressed <tt>data[i*dataIndexFactor]</tt>.
11293 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11295 // If you change something here, make sure to also adapt ::color()
11298 qDebug() << Q_FUNC_INFO << "null pointer given as data";
11303 qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11306 if (mColorBufferInvalidated)
11307 updateColorBuffer();
11311 const double posToIndexFactor = (mLevelCount-1)/range.size();
11314 for (int i=0; i<n; ++i)
11316 int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11318 index += mLevelCount;
11319 scanLine[i] = mColorBuffer.at(index);
11323 for (int i=0; i<n; ++i)
11325 int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11328 else if (index >= mLevelCount)
11329 index = mLevelCount-1;
11330 scanLine[i] = mColorBuffer.at(index);
11333 } else // logarithmic == true
11337 for (int i=0; i<n; ++i)
11339 int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11341 index += mLevelCount;
11342 scanLine[i] = mColorBuffer.at(index);
11346 for (int i=0; i<n; ++i)
11348 int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11351 else if (index >= mLevelCount)
11352 index = mLevelCount-1;
11353 scanLine[i] = mColorBuffer.at(index);
11361 This method is used to colorize a single data value given in \a position, to colors. The data
11362 range that shall be used for mapping the data value to the gradient is passed in \a range. \a
11363 logarithmic indicates whether the data value shall be mapped to a color logarithmically.
11365 If an entire array of data values shall be converted, rather use \ref colorize, for better
11368 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11370 // If you change something here, make sure to also adapt ::colorize()
11371 if (mColorBufferInvalidated)
11372 updateColorBuffer();
11375 index = (position-range.lower)*(mLevelCount-1)/range.size();
11377 index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11380 index = index % mLevelCount;
11382 index += mLevelCount;
11387 else if (index >= mLevelCount)
11388 index = mLevelCount-1;
11390 return mColorBuffer.at(index);
11394 Clears the current color stops and loads the specified \a preset. A preset consists of predefined
11395 color stops and the corresponding color interpolation method.
11397 The available presets are:
11398 \image html QCPColorGradient.png
11400 void QCPColorGradient::loadPreset(GradientPreset preset)
11406 setColorInterpolation(ciRGB);
11407 setColorStopAt(0, Qt::black);
11408 setColorStopAt(1, Qt::white);
11411 setColorInterpolation(ciRGB);
11412 setColorStopAt(0, QColor(50, 0, 0));
11413 setColorStopAt(0.2, QColor(180, 10, 0));
11414 setColorStopAt(0.4, QColor(245, 50, 0));
11415 setColorStopAt(0.6, QColor(255, 150, 10));
11416 setColorStopAt(0.8, QColor(255, 255, 50));
11417 setColorStopAt(1, QColor(255, 255, 255));
11420 setColorInterpolation(ciRGB);
11421 setColorStopAt(0, QColor(0, 0, 50));
11422 setColorStopAt(0.2, QColor(0, 10, 180));
11423 setColorStopAt(0.4, QColor(0, 50, 245));
11424 setColorStopAt(0.6, QColor(10, 150, 255));
11425 setColorStopAt(0.8, QColor(50, 255, 255));
11426 setColorStopAt(1, QColor(255, 255, 255));
11429 setColorInterpolation(ciHSV);
11430 setColorStopAt(0, QColor(10, 20, 30));
11431 setColorStopAt(1, QColor(250, 255, 250));
11434 setColorInterpolation(ciHSV);
11435 setColorStopAt(0, QColor(0, 0, 255));
11436 setColorStopAt(1, QColor(255, 250, 250));
11439 setColorInterpolation(ciRGB);
11440 setColorStopAt(0, QColor(70, 170, 210));
11441 setColorStopAt(0.20, QColor(90, 160, 180));
11442 setColorStopAt(0.25, QColor(45, 130, 175));
11443 setColorStopAt(0.30, QColor(100, 140, 125));
11444 setColorStopAt(0.5, QColor(100, 140, 100));
11445 setColorStopAt(0.6, QColor(130, 145, 120));
11446 setColorStopAt(0.7, QColor(140, 130, 120));
11447 setColorStopAt(0.9, QColor(180, 190, 190));
11448 setColorStopAt(1, QColor(210, 210, 230));
11451 setColorInterpolation(ciHSV);
11452 setColorStopAt(0, QColor(50, 10, 10));
11453 setColorStopAt(0.45, QColor(0, 0, 255));
11454 setColorStopAt(0.8, QColor(0, 255, 255));
11455 setColorStopAt(1, QColor(0, 255, 0));
11458 setColorInterpolation(ciRGB);
11459 setColorStopAt(0, QColor(0, 0, 50));
11460 setColorStopAt(0.15, QColor(20, 0, 120));
11461 setColorStopAt(0.33, QColor(200, 30, 140));
11462 setColorStopAt(0.6, QColor(255, 100, 0));
11463 setColorStopAt(0.85, QColor(255, 255, 40));
11464 setColorStopAt(1, QColor(255, 255, 255));
11467 setColorInterpolation(ciRGB);
11468 setColorStopAt(0, QColor(50, 255, 255));
11469 setColorStopAt(0.18, QColor(10, 70, 255));
11470 setColorStopAt(0.28, QColor(10, 10, 190));
11471 setColorStopAt(0.5, QColor(0, 0, 0));
11472 setColorStopAt(0.72, QColor(190, 10, 10));
11473 setColorStopAt(0.82, QColor(255, 70, 10));
11474 setColorStopAt(1, QColor(255, 255, 50));
11477 setColorInterpolation(ciHSV);
11478 setColorStopAt(0, QColor(50, 0, 50));
11479 setColorStopAt(0.15, QColor(0, 0, 255));
11480 setColorStopAt(0.35, QColor(0, 255, 255));
11481 setColorStopAt(0.6, QColor(255, 255, 0));
11482 setColorStopAt(0.75, QColor(255, 30, 0));
11483 setColorStopAt(1, QColor(50, 0, 0));
11486 setColorInterpolation(ciRGB);
11487 setColorStopAt(0, QColor(0, 0, 100));
11488 setColorStopAt(0.15, QColor(0, 50, 255));
11489 setColorStopAt(0.35, QColor(0, 255, 255));
11490 setColorStopAt(0.65, QColor(255, 255, 0));
11491 setColorStopAt(0.85, QColor(255, 30, 0));
11492 setColorStopAt(1, QColor(100, 0, 0));
11495 setColorInterpolation(ciHSV);
11496 setColorStopAt(0, QColor(255, 0, 0));
11497 setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11498 setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11499 setColorStopAt(1, QColor(255, 0, 0));
11505 Clears all color stops.
11507 \see setColorStops, setColorStopAt
11509 void QCPColorGradient::clearColorStops()
11511 mColorStops.clear();
11512 mColorBufferInvalidated = true;
11516 Returns an inverted gradient. The inverted gradient has all properties as this \ref
11517 QCPColorGradient, but the order of the color stops is inverted.
11519 \see setColorStops, setColorStopAt
11521 QCPColorGradient QCPColorGradient::inverted() const
11523 QCPColorGradient result(*this);
11524 result.clearColorStops();
11525 for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11526 result.setColorStopAt(1.0-it.key(), it.value());
11532 Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
11533 convert positions to colors. This is where the interpolation between color stops is calculated.
11535 void QCPColorGradient::updateColorBuffer()
11537 if (mColorBuffer.size() != mLevelCount)
11538 mColorBuffer.resize(mLevelCount);
11539 if (mColorStops.size() > 1)
11541 double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11542 for (int i=0; i<mLevelCount; ++i)
11544 double position = i*indexToPosFactor;
11545 QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11546 if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11548 mColorBuffer[i] = (it-1).value().rgb();
11549 } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11551 mColorBuffer[i] = it.value().rgb();
11552 } else // position is in between stops (or on an intermediate stop), interpolate color
11554 QMap<double, QColor>::const_iterator high = it;
11555 QMap<double, QColor>::const_iterator low = it-1;
11556 double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11557 switch (mColorInterpolation)
11561 mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11562 (1-t)*low.value().green() + t*high.value().green(),
11563 (1-t)*low.value().blue() + t*high.value().blue());
11568 QColor lowHsv = low.value().toHsv();
11569 QColor highHsv = high.value().toHsv();
11571 double hueDiff = highHsv.hueF()-lowHsv.hueF();
11573 hue = lowHsv.hueF() - t*(1.0-hueDiff);
11574 else if (hueDiff < -0.5)
11575 hue = lowHsv.hueF() + t*(1.0+hueDiff);
11577 hue = lowHsv.hueF() + t*hueDiff;
11578 if (hue < 0) hue += 1.0;
11579 else if (hue >= 1.0) hue -= 1.0;
11580 mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11586 } else if (mColorStops.size() == 1)
11588 mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11589 } else // mColorStops is empty, fill color buffer with black
11591 mColorBuffer.fill(qRgb(0, 0, 0));
11593 mColorBufferInvalidated = false;
11597 ////////////////////////////////////////////////////////////////////////////////////////////////////
11598 //////////////////// QCPAxisRect
11599 ////////////////////////////////////////////////////////////////////////////////////////////////////
11601 /*! \class QCPAxisRect
11602 \brief Holds multiple axes and arranges them in a rectangular shape.
11604 This class represents an axis rect, a rectangular area that is bounded on all sides with an
11605 arbitrary number of axes.
11607 Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
11608 layout system allows to have multiple axis rects, e.g. arranged in a grid layout
11609 (QCustomPlot::plotLayout).
11611 By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
11612 accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
11613 If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
11614 invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
11615 addAxes. To remove an axis, use \ref removeAxis.
11617 The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
11618 setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
11619 explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
11620 placed on other layers, independently of the axis rect.
11622 Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
11623 insetLayout and can be used to have other layout elements (or even other layouts with multiple
11624 elements) hovering inside the axis rect.
11626 If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
11627 behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
11628 is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
11629 via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
11630 only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
11633 \image html AxisRectSpacingOverview.png
11634 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
11635 line on the far left indicates the viewport/widget border.</center>
11638 /* start documentation of inline functions */
11640 /*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
11642 Returns the inset layout of this axis rect. It can be used to place other layout elements (or
11643 even layouts with multiple other elements) inside/on top of an axis rect.
11645 \see QCPLayoutInset
11648 /*! \fn int QCPAxisRect::left() const
11650 Returns the pixel position of the left border of this axis rect. Margins are not taken into
11651 account here, so the returned value is with respect to the inner \ref rect.
11654 /*! \fn int QCPAxisRect::right() const
11656 Returns the pixel position of the right border of this axis rect. Margins are not taken into
11657 account here, so the returned value is with respect to the inner \ref rect.
11660 /*! \fn int QCPAxisRect::top() const
11662 Returns the pixel position of the top border of this axis rect. Margins are not taken into
11663 account here, so the returned value is with respect to the inner \ref rect.
11666 /*! \fn int QCPAxisRect::bottom() const
11668 Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
11669 account here, so the returned value is with respect to the inner \ref rect.
11672 /*! \fn int QCPAxisRect::width() const
11674 Returns the pixel width of this axis rect. Margins are not taken into account here, so the
11675 returned value is with respect to the inner \ref rect.
11678 /*! \fn int QCPAxisRect::height() const
11680 Returns the pixel height of this axis rect. Margins are not taken into account here, so the
11681 returned value is with respect to the inner \ref rect.
11684 /*! \fn QSize QCPAxisRect::size() const
11686 Returns the pixel size of this axis rect. Margins are not taken into account here, so the
11687 returned value is with respect to the inner \ref rect.
11690 /*! \fn QPoint QCPAxisRect::topLeft() const
11692 Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
11693 so the returned value is with respect to the inner \ref rect.
11696 /*! \fn QPoint QCPAxisRect::topRight() const
11698 Returns the top right corner of this axis rect in pixels. Margins are not taken into account
11699 here, so the returned value is with respect to the inner \ref rect.
11702 /*! \fn QPoint QCPAxisRect::bottomLeft() const
11704 Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
11705 here, so the returned value is with respect to the inner \ref rect.
11708 /*! \fn QPoint QCPAxisRect::bottomRight() const
11710 Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
11711 here, so the returned value is with respect to the inner \ref rect.
11714 /*! \fn QPoint QCPAxisRect::center() const
11716 Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
11717 returned value is with respect to the inner \ref rect.
11720 /* end documentation of inline functions */
11723 Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
11724 sides, the top and right axes are set invisible initially.
11726 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11727 QCPLayoutElement(parentPlot),
11728 mBackgroundBrush(Qt::NoBrush),
11729 mBackgroundScaled(true),
11730 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11731 mInsetLayout(new QCPLayoutInset),
11732 mRangeDrag(Qt::Horizontal|Qt::Vertical),
11733 mRangeZoom(Qt::Horizontal|Qt::Vertical),
11734 mRangeZoomFactorHorz(0.85),
11735 mRangeZoomFactorVert(0.85),
11738 mInsetLayout->initializeParentPlot(mParentPlot);
11739 mInsetLayout->setParentLayerable(this);
11740 mInsetLayout->setParent(this);
11742 setMinimumSize(50, 50);
11743 setMinimumMargins(QMargins(15, 15, 15, 15));
11744 mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11745 mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11746 mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11747 mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11749 if (setupDefaultAxes)
11751 QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11752 QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11753 QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11754 QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11755 setRangeDragAxes(xAxis, yAxis);
11756 setRangeZoomAxes(xAxis, yAxis);
11757 xAxis2->setVisible(false);
11758 yAxis2->setVisible(false);
11759 xAxis->grid()->setVisible(true);
11760 yAxis->grid()->setVisible(true);
11761 xAxis2->grid()->setVisible(false);
11762 yAxis2->grid()->setVisible(false);
11763 xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11764 yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11765 xAxis2->grid()->setVisible(false);
11766 yAxis2->grid()->setVisible(false);
11770 QCPAxisRect::~QCPAxisRect()
11772 delete mInsetLayout;
11775 QList<QCPAxis*> axesList = axes();
11776 for (int i=0; i<axesList.size(); ++i)
11777 removeAxis(axesList.at(i));
11781 Returns the number of axes on the axis rect side specified with \a type.
11785 int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
11787 return mAxes.value(type).size();
11791 Returns the axis with the given \a index on the axis rect side specified with \a type.
11793 \see axisCount, axes
11795 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
11797 QList<QCPAxis*> ax(mAxes.value(type));
11798 if (index >= 0 && index < ax.size())
11800 return ax.at(index);
11803 qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11809 Returns all axes on the axis rect sides specified with \a types.
11811 \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
11816 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11818 QList<QCPAxis*> result;
11819 if (types.testFlag(QCPAxis::atLeft))
11820 result << mAxes.value(QCPAxis::atLeft);
11821 if (types.testFlag(QCPAxis::atRight))
11822 result << mAxes.value(QCPAxis::atRight);
11823 if (types.testFlag(QCPAxis::atTop))
11824 result << mAxes.value(QCPAxis::atTop);
11825 if (types.testFlag(QCPAxis::atBottom))
11826 result << mAxes.value(QCPAxis::atBottom);
11832 Returns all axes of this axis rect.
11834 QList<QCPAxis*> QCPAxisRect::axes() const
11836 QList<QCPAxis*> result;
11837 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11838 while (it.hasNext())
11841 result << it.value();
11847 Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a
11848 new QCPAxis instance is created internally.
11850 You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis to an axis that was
11851 previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
11852 of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
11853 with this axis rect as parent and with the same axis type as specified in \a type. If this is not
11854 the case, a debug output is generated, the axis is not added, and the method returns 0.
11856 This method can not be used to move \a axis between axis rects. The same \a axis instance must
11857 not be added multiple times to the same or different axis rects.
11859 If an axis rect side already contains one or more axes, the lower and upper endings of the new
11860 axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref
11861 QCPLineEnding::esHalfBar.
11863 \see addAxes, setupFullAxesBox
11865 QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
11867 QCPAxis *newAxis = axis;
11870 newAxis = new QCPAxis(this, type);
11871 } else // user provided existing axis instance, do some sanity checks
11873 if (newAxis->axisType() != type)
11875 qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
11878 if (newAxis->axisRect() != this)
11880 qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
11883 if (axes().contains(newAxis))
11885 qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
11889 if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11891 bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11892 newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11893 newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11895 mAxes[type].append(newAxis);
11900 Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
11901 <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
11903 Returns a list of the added axes.
11905 \see addAxis, setupFullAxesBox
11907 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11909 QList<QCPAxis*> result;
11910 if (types.testFlag(QCPAxis::atLeft))
11911 result << addAxis(QCPAxis::atLeft);
11912 if (types.testFlag(QCPAxis::atRight))
11913 result << addAxis(QCPAxis::atRight);
11914 if (types.testFlag(QCPAxis::atTop))
11915 result << addAxis(QCPAxis::atTop);
11916 if (types.testFlag(QCPAxis::atBottom))
11917 result << addAxis(QCPAxis::atBottom);
11922 Removes the specified \a axis from the axis rect and deletes it.
11924 Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
11928 bool QCPAxisRect::removeAxis(QCPAxis *axis)
11930 // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11931 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11932 while (it.hasNext())
11935 if (it.value().contains(axis))
11937 mAxes[it.key()].removeOne(axis);
11938 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)
11939 parentPlot()->axisRemoved(axis);
11944 qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11949 Convenience function to create an axis on each side that doesn't have any axes yet and set their
11950 visibility to true. Further, the top/right axes are assigned the following properties of the
11953 \li range (\ref QCPAxis::setRange)
11954 \li range reversed (\ref QCPAxis::setRangeReversed)
11955 \li scale type (\ref QCPAxis::setScaleType)
11956 \li scale log base (\ref QCPAxis::setScaleLogBase)
11957 \li ticks (\ref QCPAxis::setTicks)
11958 \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
11959 \li sub tick count (\ref QCPAxis::setSubTickCount)
11960 \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
11961 \li tick step (\ref QCPAxis::setTickStep)
11962 \li auto tick step (\ref QCPAxis::setAutoTickStep)
11963 \li number format (\ref QCPAxis::setNumberFormat)
11964 \li number precision (\ref QCPAxis::setNumberPrecision)
11965 \li tick label type (\ref QCPAxis::setTickLabelType)
11966 \li date time format (\ref QCPAxis::setDateTimeFormat)
11967 \li date time spec (\ref QCPAxis::setDateTimeSpec)
11969 Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
11971 If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
11972 and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
11974 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11976 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11977 if (axisCount(QCPAxis::atBottom) == 0)
11978 xAxis = addAxis(QCPAxis::atBottom);
11980 xAxis = axis(QCPAxis::atBottom);
11982 if (axisCount(QCPAxis::atLeft) == 0)
11983 yAxis = addAxis(QCPAxis::atLeft);
11985 yAxis = axis(QCPAxis::atLeft);
11987 if (axisCount(QCPAxis::atTop) == 0)
11988 xAxis2 = addAxis(QCPAxis::atTop);
11990 xAxis2 = axis(QCPAxis::atTop);
11992 if (axisCount(QCPAxis::atRight) == 0)
11993 yAxis2 = addAxis(QCPAxis::atRight);
11995 yAxis2 = axis(QCPAxis::atRight);
11997 xAxis->setVisible(true);
11998 yAxis->setVisible(true);
11999 xAxis2->setVisible(true);
12000 yAxis2->setVisible(true);
12001 xAxis2->setTickLabels(false);
12002 yAxis2->setTickLabels(false);
12004 xAxis2->setRange(xAxis->range());
12005 xAxis2->setRangeReversed(xAxis->rangeReversed());
12006 xAxis2->setScaleType(xAxis->scaleType());
12007 xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12008 xAxis2->setTicks(xAxis->ticks());
12009 xAxis2->setAutoTickCount(xAxis->autoTickCount());
12010 xAxis2->setSubTickCount(xAxis->subTickCount());
12011 xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12012 xAxis2->setTickStep(xAxis->tickStep());
12013 xAxis2->setAutoTickStep(xAxis->autoTickStep());
12014 xAxis2->setNumberFormat(xAxis->numberFormat());
12015 xAxis2->setNumberPrecision(xAxis->numberPrecision());
12016 xAxis2->setTickLabelType(xAxis->tickLabelType());
12017 xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12018 xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12020 yAxis2->setRange(yAxis->range());
12021 yAxis2->setRangeReversed(yAxis->rangeReversed());
12022 yAxis2->setScaleType(yAxis->scaleType());
12023 yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12024 yAxis2->setTicks(yAxis->ticks());
12025 yAxis2->setAutoTickCount(yAxis->autoTickCount());
12026 yAxis2->setSubTickCount(yAxis->subTickCount());
12027 yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12028 yAxis2->setTickStep(yAxis->tickStep());
12029 yAxis2->setAutoTickStep(yAxis->autoTickStep());
12030 yAxis2->setNumberFormat(yAxis->numberFormat());
12031 yAxis2->setNumberPrecision(yAxis->numberPrecision());
12032 yAxis2->setTickLabelType(yAxis->tickLabelType());
12033 yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12034 yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12038 connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12039 connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12044 Returns a list of all the plottables that are associated with this axis rect.
12046 A plottable is considered associated with an axis rect if its key or value axis (or both) is in
12051 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12053 // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12054 QList<QCPAbstractPlottable*> result;
12055 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12057 if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12058 result.append(mParentPlot->mPlottables.at(i));
12064 Returns a list of all the graphs that are associated with this axis rect.
12066 A graph is considered associated with an axis rect if its key or value axis (or both) is in
12069 \see plottables, items
12071 QList<QCPGraph*> QCPAxisRect::graphs() const
12073 // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12074 QList<QCPGraph*> result;
12075 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12077 if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12078 result.append(mParentPlot->mGraphs.at(i));
12084 Returns a list of all the items that are associated with this axis rect.
12086 An item is considered associated with an axis rect if any of its positions has key or value axis
12087 set to an axis that is in this axis rect, or if any of its positions has \ref
12088 QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
12089 QCPAbstractItem::setClipAxisRect) is set to this axis rect.
12091 \see plottables, graphs
12093 QList<QCPAbstractItem *> QCPAxisRect::items() const
12095 // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12096 // and miss those items that have this axis rect as clipAxisRect.
12097 QList<QCPAbstractItem*> result;
12098 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12100 if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12102 result.append(mParentPlot->mItems.at(itemId));
12105 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12106 for (int posId=0; posId<positions.size(); ++posId)
12108 if (positions.at(posId)->axisRect() == this ||
12109 positions.at(posId)->keyAxis()->axisRect() == this ||
12110 positions.at(posId)->valueAxis()->axisRect() == this)
12112 result.append(mParentPlot->mItems.at(itemId));
12121 This method is called automatically upon replot and doesn't need to be called by users of
12124 Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
12125 and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
12126 QCPInsetLayout::update function.
12128 void QCPAxisRect::update(UpdatePhase phase)
12130 QCPLayoutElement::update(phase);
12134 case upPreparation:
12136 QList<QCPAxis*> allAxes = axes();
12137 for (int i=0; i<allAxes.size(); ++i)
12138 allAxes.at(i)->setupTickVectors();
12143 mInsetLayout->setOuterRect(rect());
12149 // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12150 mInsetLayout->update(phase);
12153 /* inherits documentation from base class */
12154 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12156 QList<QCPLayoutElement*> result;
12159 result << mInsetLayout;
12161 result << mInsetLayout->elements(recursive);
12166 /* inherits documentation from base class */
12167 void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
12169 painter->setAntialiasing(false);
12172 /* inherits documentation from base class */
12173 void QCPAxisRect::draw(QCPPainter *painter)
12175 drawBackground(painter);
12179 Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
12180 axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
12181 backgrounds are usually drawn below everything else.
12183 For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
12184 enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
12185 is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
12186 consider using the overloaded version of this function.
12188 Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
12189 setBackground(const QBrush &brush).
12191 \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
12193 void QCPAxisRect::setBackground(const QPixmap &pm)
12195 mBackgroundPixmap = pm;
12196 mScaledBackgroundPixmap = QPixmap();
12201 Sets \a brush as the background brush. The axis rect background will be filled with this brush.
12202 Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
12203 are usually drawn below everything else.
12205 The brush will be drawn before (under) any background pixmap, which may be specified with \ref
12206 setBackground(const QPixmap &pm).
12208 To disable drawing of a background brush, set \a brush to Qt::NoBrush.
12210 \see setBackground(const QPixmap &pm)
12212 void QCPAxisRect::setBackground(const QBrush &brush)
12214 mBackgroundBrush = brush;
12219 Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
12220 shall be scaled in one call.
12222 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
12224 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12226 mBackgroundPixmap = pm;
12227 mScaledBackgroundPixmap = QPixmap();
12228 mBackgroundScaled = scaled;
12229 mBackgroundScaledMode = mode;
12233 Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
12234 is set to true, you may control whether and how the aspect ratio of the original pixmap is
12235 preserved with \ref setBackgroundScaledMode.
12237 Note that the scaled version of the original pixmap is buffered, so there is no performance
12238 penalty on replots. (Except when the axis rect dimensions are changed continuously.)
12240 \see setBackground, setBackgroundScaledMode
12242 void QCPAxisRect::setBackgroundScaled(bool scaled)
12244 mBackgroundScaled = scaled;
12248 If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
12249 define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
12250 \see setBackground, setBackgroundScaled
12252 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12254 mBackgroundScaledMode = mode;
12258 Returns the range drag axis of the \a orientation provided.
12260 \see setRangeDragAxes
12262 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12264 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12268 Returns the range zoom axis of the \a orientation provided.
12270 \see setRangeZoomAxes
12272 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12274 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12278 Returns the range zoom factor of the \a orientation provided.
12280 \see setRangeZoomFactor
12282 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12284 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12288 Sets which axis orientation may be range dragged by the user with mouse interaction.
12289 What orientation corresponds to which specific axis can be set with
12290 \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
12291 default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
12292 is the left axis (yAxis).
12294 To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
12295 QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
12296 Qt::Vertical</tt> as \a orientations.
12298 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12299 contains \ref QCP::iRangeDrag to enable the range dragging interaction.
12301 \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag
12303 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12305 mRangeDrag = orientations;
12309 Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
12310 corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
12311 QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
12312 axis is the left axis (yAxis).
12314 To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
12315 QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
12316 Qt::Vertical</tt> as \a orientations.
12318 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12319 contains \ref QCP::iRangeZoom to enable the range zooming interaction.
12321 \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
12323 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12325 mRangeZoom = orientations;
12329 Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
12330 on the QCustomPlot widget.
12332 \see setRangeZoomAxes
12334 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12336 mRangeDragHorzAxis = horizontal;
12337 mRangeDragVertAxis = vertical;
12341 Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
12342 QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
12343 are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
12345 \see setRangeDragAxes
12347 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12349 mRangeZoomHorzAxis = horizontal;
12350 mRangeZoomVertAxis = vertical;
12354 Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
12355 \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
12356 let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
12357 and which is vertical, can be set with \ref setRangeZoomAxes.
12359 When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
12360 will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
12361 same scrolling direction will zoom out.
12363 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12365 mRangeZoomFactorHorz = horizontalFactor;
12366 mRangeZoomFactorVert = verticalFactor;
12371 Sets both the horizontal and vertical zoom \a factor.
12373 void QCPAxisRect::setRangeZoomFactor(double factor)
12375 mRangeZoomFactorHorz = factor;
12376 mRangeZoomFactorVert = factor;
12381 Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
12384 If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
12385 according filling inside the axis rect with the provided \a painter.
12387 Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
12388 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
12389 the axis rect with the provided \a painter. The scaled version is buffered in
12390 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
12391 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
12392 dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
12395 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
12397 void QCPAxisRect::drawBackground(QCPPainter *painter)
12399 // draw background fill:
12400 if (mBackgroundBrush != Qt::NoBrush)
12401 painter->fillRect(mRect, mBackgroundBrush);
12403 // draw background pixmap (on top of fill, if brush specified):
12404 if (!mBackgroundPixmap.isNull())
12406 if (mBackgroundScaled)
12408 // check whether mScaledBackground needs to be updated:
12409 QSize scaledSize(mBackgroundPixmap.size());
12410 scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12411 if (mScaledBackgroundPixmap.size() != scaledSize)
12412 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12413 painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12416 painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12423 This function makes sure multiple axes on the side specified with \a type don't collide, but are
12424 distributed according to their respective space requirement (QCPAxis::calculateMargin).
12426 It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
12427 one with index zero.
12429 This function is called by \ref calculateAutoMargin.
12431 void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
12433 const QList<QCPAxis*> axesList = mAxes.value(type);
12434 if (axesList.isEmpty())
12437 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
12438 for (int i=1; i<axesList.size(); ++i)
12440 int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12441 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)
12443 if (!isFirstVisible)
12444 offset += axesList.at(i)->tickLengthIn();
12445 isFirstVisible = false;
12447 axesList.at(i)->setOffset(offset);
12451 /* inherits documentation from base class */
12452 int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
12454 if (!mAutoMargins.testFlag(side))
12455 qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12457 updateAxesOffset(QCPAxis::marginSideToAxisType(side));
12459 // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12460 const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12461 if (axesList.size() > 0)
12462 return axesList.last()->offset() + axesList.last()->calculateMargin();
12469 Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
12470 pressed, the range dragging interaction is initialized (the actual range manipulation happens in
12471 the \ref mouseMoveEvent).
12473 The mDragging flag is set to true and some anchor points are set that are needed to determine the
12474 distance the mouse was dragged in the mouse move/release events later.
12476 \see mouseMoveEvent, mouseReleaseEvent
12478 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12480 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)
12481 if (event->buttons() & Qt::LeftButton)
12484 // initialize antialiasing backup in case we start dragging:
12485 if (mParentPlot->noAntialiasingOnDrag())
12487 mAADragBackup = mParentPlot->antialiasedElements();
12488 mNotAADragBackup = mParentPlot->notAntialiasedElements();
12490 // Mouse range dragging interaction:
12491 if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12493 if (mRangeDragHorzAxis)
12494 mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12495 if (mRangeDragVertAxis)
12496 mDragStartVertRange = mRangeDragVertAxis.data()->range();
12503 Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
12504 preceding \ref mousePressEvent, the range is moved accordingly.
12506 \see mousePressEvent, mouseReleaseEvent
12508 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12510 // Mouse range dragging interaction:
12511 if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12513 if (mRangeDrag.testFlag(Qt::Horizontal))
12515 if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12517 if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12519 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12520 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12521 } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12523 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12524 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12528 if (mRangeDrag.testFlag(Qt::Vertical))
12530 if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12532 if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12534 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12535 rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12536 } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12538 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12539 rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12543 if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12545 if (mParentPlot->noAntialiasingOnDrag())
12546 mParentPlot->setNotAntialiasedElements(QCP::aeAll);
12547 mParentPlot->replot();
12552 /* inherits documentation from base class */
12553 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12557 if (mParentPlot->noAntialiasingOnDrag())
12559 mParentPlot->setAntialiasedElements(mAADragBackup);
12560 mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
12566 Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
12567 ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
12568 the scaling operation is the current cursor position inside the axis rect. The scaling factor is
12569 dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
12570 zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
12572 Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
12573 wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
12574 multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
12575 exponent of the range zoom factor. This takes care of the wheel direction automatically, by
12576 inverting the factor, when the wheel step is negative (f^-1 = 1/f).
12578 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12580 // Mouse range zooming interaction:
12581 if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12583 if (mRangeZoom != 0)
12586 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12587 if (mRangeZoom.testFlag(Qt::Horizontal))
12589 factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12590 if (mRangeZoomHorzAxis.data())
12591 mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12593 if (mRangeZoom.testFlag(Qt::Vertical))
12595 factor = qPow(mRangeZoomFactorVert, wheelSteps);
12596 if (mRangeZoomVertAxis.data())
12597 mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12599 mParentPlot->replot();
12605 ////////////////////////////////////////////////////////////////////////////////////////////////////
12606 //////////////////// QCPAbstractLegendItem
12607 ////////////////////////////////////////////////////////////////////////////////////////////////////
12609 /*! \class QCPAbstractLegendItem
12610 \brief The abstract base class for all entries in a QCPLegend.
12612 It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
12613 legend, the subclass \ref QCPPlottableLegendItem is more suitable.
12615 Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
12616 that's not even associated with a plottable).
12618 You must implement the following pure virtual functions:
12619 \li \ref draw (from QCPLayerable)
12621 You inherit the following members you may use:
12624 <td>QCPLegend *\b mParentLegend</td>
12625 <td>A pointer to the parent QCPLegend.</td>
12627 <td>QFont \b mFont</td>
12628 <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>
12633 /* start of documentation of signals */
12635 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
12637 This signal is emitted when the selection state of this legend item has changed, either by user
12638 interaction or by a direct call to \ref setSelected.
12641 /* end of documentation of signals */
12644 Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
12645 cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
12647 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
12648 QCPLayoutElement(parent->parentPlot()),
12649 mParentLegend(parent),
12650 mFont(parent->font()),
12651 mTextColor(parent->textColor()),
12652 mSelectedFont(parent->selectedFont()),
12653 mSelectedTextColor(parent->selectedTextColor()),
12657 setLayer(QLatin1String("legend"));
12658 setMargins(QMargins(8, 2, 8, 2));
12662 Sets the default font of this specific legend item to \a font.
12664 \see setTextColor, QCPLegend::setFont
12666 void QCPAbstractLegendItem::setFont(const QFont &font)
12672 Sets the default text color of this specific legend item to \a color.
12674 \see setFont, QCPLegend::setTextColor
12676 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12678 mTextColor = color;
12682 When this legend item is selected, \a font is used to draw generic text, instead of the normal
12683 font set with \ref setFont.
12685 \see setFont, QCPLegend::setSelectedFont
12687 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
12689 mSelectedFont = font;
12693 When this legend item is selected, \a color is used to draw generic text, instead of the normal
12694 color set with \ref setTextColor.
12696 \see setTextColor, QCPLegend::setSelectedTextColor
12698 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
12700 mSelectedTextColor = color;
12704 Sets whether this specific legend item is selectable.
12706 \see setSelectedParts, QCustomPlot::setInteractions
12708 void QCPAbstractLegendItem::setSelectable(bool selectable)
12710 if (mSelectable != selectable)
12712 mSelectable = selectable;
12713 emit selectableChanged(mSelectable);
12718 Sets whether this specific legend item is selected.
12720 It is possible to set the selection state of this item by calling this function directly, even if
12721 setSelectable is set to false.
12723 \see setSelectableParts, QCustomPlot::setInteractions
12725 void QCPAbstractLegendItem::setSelected(bool selected)
12727 if (mSelected != selected)
12729 mSelected = selected;
12730 emit selectionChanged(mSelected);
12734 /* inherits documentation from base class */
12735 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12738 if (!mParentPlot) return -1;
12739 if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12742 if (mRect.contains(pos.toPoint()))
12743 return mParentPlot->selectionTolerance()*0.99;
12748 /* inherits documentation from base class */
12749 void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
12751 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
12754 /* inherits documentation from base class */
12755 QRect QCPAbstractLegendItem::clipRect() const
12760 /* inherits documentation from base class */
12761 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12765 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12767 bool selBefore = mSelected;
12768 setSelected(additive ? !mSelected : true);
12769 if (selectionStateChanged)
12770 *selectionStateChanged = mSelected != selBefore;
12774 /* inherits documentation from base class */
12775 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12777 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12779 bool selBefore = mSelected;
12780 setSelected(false);
12781 if (selectionStateChanged)
12782 *selectionStateChanged = mSelected != selBefore;
12786 ////////////////////////////////////////////////////////////////////////////////////////////////////
12787 //////////////////// QCPPlottableLegendItem
12788 ////////////////////////////////////////////////////////////////////////////////////////////////////
12790 /*! \class QCPPlottableLegendItem
12791 \brief A legend item representing a plottable with an icon and the plottable name.
12793 This is the standard legend item for plottables. It displays an icon of the plottable next to the
12794 plottable name. The icon is drawn by the respective plottable itself (\ref
12795 QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
12796 For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
12799 Legend items of this type are always associated with one plottable (retrievable via the
12800 plottable() function and settable with the constructor). You may change the font of the plottable
12801 name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
12802 QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
12804 The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
12805 creates/removes legend items of this type in the default implementation. However, these functions
12806 may be reimplemented such that a different kind of legend item (e.g a direct subclass of
12807 QCPAbstractLegendItem) is used for that plottable.
12809 Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
12810 QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
12811 interface, QCPLegend has specialized functions for handling legend items conveniently, see the
12812 documentation of \ref QCPLegend.
12816 Creates a new legend item associated with \a plottable.
12818 Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
12820 A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
12821 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
12823 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
12824 QCPAbstractLegendItem(parent),
12825 mPlottable(plottable)
12831 Returns the pen that shall be used to draw the icon border, taking into account the selection
12832 state of this item.
12834 QPen QCPPlottableLegendItem::getIconBorderPen() const
12836 return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
12841 Returns the text color that shall be used to draw text, taking into account the selection state
12844 QColor QCPPlottableLegendItem::getTextColor() const
12846 return mSelected ? mSelectedTextColor : mTextColor;
12851 Returns the font that shall be used to draw text, taking into account the selection state of this
12854 QFont QCPPlottableLegendItem::getFont() const
12856 return mSelected ? mSelectedFont : mFont;
12861 Draws the item with \a painter. The size and position of the drawn legend item is defined by the
12862 parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
12863 of this legend item.
12865 void QCPPlottableLegendItem::draw(QCPPainter *painter)
12867 if (!mPlottable) return;
12868 painter->setFont(getFont());
12869 painter->setPen(QPen(getTextColor()));
12870 QSizeF iconSize = mParentLegend->iconSize();
12871 QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12872 QRectF iconRect(mRect.topLeft(), iconSize);
12873 int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12874 painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12877 painter->setClipRect(iconRect, Qt::IntersectClip);
12878 mPlottable->drawLegendIcon(painter, iconRect);
12879 painter->restore();
12880 // draw icon border:
12881 if (getIconBorderPen().style() != Qt::NoPen)
12883 painter->setPen(getIconBorderPen());
12884 painter->setBrush(Qt::NoBrush);
12885 painter->drawRect(iconRect);
12891 Calculates and returns the size of this item. This includes the icon, the text and the padding in
12894 QSize QCPPlottableLegendItem::minimumSizeHint() const
12896 if (!mPlottable) return QSize();
12897 QSize result(0, 0);
12899 QFontMetrics fontMetrics(getFont());
12900 QSize iconSize = mParentLegend->iconSize();
12901 textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12902 result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12903 result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12908 ////////////////////////////////////////////////////////////////////////////////////////////////////
12909 //////////////////// QCPLegend
12910 ////////////////////////////////////////////////////////////////////////////////////////////////////
12912 /*! \class QCPLegend
12913 \brief Manages a legend inside a QCustomPlot.
12915 A legend is a small box somewhere in the plot which lists plottables with their name and icon.
12917 Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
12918 respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
12919 QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
12920 itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
12922 The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
12923 QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
12924 placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
12925 handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
12926 other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
12927 However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
12928 only return the number of items with QCPAbstractLegendItems type).
12930 By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
12931 layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
12932 position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
12933 outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
12936 /* start of documentation of signals */
12938 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
12940 This signal is emitted when the selection state of this legend has changed.
12942 \see setSelectedParts, setSelectableParts
12945 /* end of documentation of signals */
12948 Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
12950 Note that by default, QCustomPlot already contains a legend ready to be used as
12951 QCustomPlot::legend
12953 QCPLegend::QCPLegend()
12956 setColumnSpacing(10);
12957 setMargins(QMargins(2, 3, 2, 2));
12958 setAntialiased(false);
12959 setIconSize(32, 18);
12961 setIconTextPadding(7);
12963 setSelectableParts(spLegendBox | spItems);
12964 setSelectedParts(spNone);
12966 setBorderPen(QPen(Qt::black));
12967 setSelectedBorderPen(QPen(Qt::blue, 2));
12968 setIconBorderPen(Qt::NoPen);
12969 setSelectedIconBorderPen(QPen(Qt::blue, 2));
12970 setBrush(Qt::white);
12971 setSelectedBrush(Qt::white);
12972 setTextColor(Qt::black);
12973 setSelectedTextColor(Qt::blue);
12976 QCPLegend::~QCPLegend()
12979 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)
12980 mParentPlot->legendRemoved(this);
12983 /* no doc for getter, see setSelectedParts */
12984 QCPLegend::SelectableParts QCPLegend::selectedParts() const
12986 // check whether any legend elements selected, if yes, add spItems to return value
12987 bool hasSelectedItems = false;
12988 for (int i=0; i<itemCount(); ++i)
12990 if (item(i) && item(i)->selected())
12992 hasSelectedItems = true;
12996 if (hasSelectedItems)
12997 return mSelectedParts | spItems;
12999 return mSelectedParts & ~spItems;
13003 Sets the pen, the border of the entire legend is drawn with.
13005 void QCPLegend::setBorderPen(const QPen &pen)
13011 Sets the brush of the legend background.
13013 void QCPLegend::setBrush(const QBrush &brush)
13019 Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
13020 use this font by default. However, a different font can be specified on a per-item-basis by
13021 accessing the specific legend item.
13023 This function will also set \a font on all already existing legend items.
13025 \see QCPAbstractLegendItem::setFont
13027 void QCPLegend::setFont(const QFont &font)
13030 for (int i=0; i<itemCount(); ++i)
13033 item(i)->setFont(mFont);
13038 Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
13039 will use this color by default. However, a different colors can be specified on a per-item-basis
13040 by accessing the specific legend item.
13042 This function will also set \a color on all already existing legend items.
13044 \see QCPAbstractLegendItem::setTextColor
13046 void QCPLegend::setTextColor(const QColor &color)
13048 mTextColor = color;
13049 for (int i=0; i<itemCount(); ++i)
13052 item(i)->setTextColor(color);
13057 Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
13058 representation of the graph) will use this size by default.
13060 void QCPLegend::setIconSize(const QSize &size)
13067 void QCPLegend::setIconSize(int width, int height)
13069 mIconSize.setWidth(width);
13070 mIconSize.setHeight(height);
13074 Sets the horizontal space in pixels between the legend icon and the text next to it.
13075 Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
13076 name of the graph) will use this space by default.
13078 void QCPLegend::setIconTextPadding(int padding)
13080 mIconTextPadding = padding;
13084 Sets the pen used to draw a border around each legend icon. Legend items that draw an
13085 icon (e.g. a visual representation of the graph) will use this pen by default.
13087 If no border is wanted, set this to \a Qt::NoPen.
13089 void QCPLegend::setIconBorderPen(const QPen &pen)
13091 mIconBorderPen = pen;
13095 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
13096 (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
13098 However, even when \a selectable is set to a value not allowing the selection of a specific part,
13099 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
13102 \see SelectablePart, setSelectedParts
13104 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13106 if (mSelectableParts != selectable)
13108 mSelectableParts = selectable;
13109 emit selectableChanged(mSelectableParts);
13114 Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
13115 is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
13116 doesn't contain \ref spItems, those items become deselected.
13118 The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
13119 contains iSelectLegend. You only need to call this function when you wish to change the selection
13122 This function can change the selection state of a part even when \ref setSelectableParts was set to a
13123 value that actually excludes the part.
13125 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
13127 Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
13128 before, because there's no way to specify which exact items to newly select. Do this by calling
13129 \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
13131 \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
13134 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13136 SelectableParts newSelected = selected;
13137 mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13139 if (mSelectedParts != newSelected)
13141 if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13143 qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13144 newSelected &= ~spItems;
13146 if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13148 for (int i=0; i<itemCount(); ++i)
13151 item(i)->setSelected(false);
13154 mSelectedParts = newSelected;
13155 emit selectionChanged(mSelectedParts);
13160 When the legend box is selected, this pen is used to draw the border instead of the normal pen
13161 set via \ref setBorderPen.
13163 \see setSelectedParts, setSelectableParts, setSelectedBrush
13165 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13167 mSelectedBorderPen = pen;
13171 Sets the pen legend items will use to draw their icon borders, when they are selected.
13173 \see setSelectedParts, setSelectableParts, setSelectedFont
13175 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
13177 mSelectedIconBorderPen = pen;
13181 When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
13182 set via \ref setBrush.
13184 \see setSelectedParts, setSelectableParts, setSelectedBorderPen
13186 void QCPLegend::setSelectedBrush(const QBrush &brush)
13188 mSelectedBrush = brush;
13192 Sets the default font that is used by legend items when they are selected.
13194 This function will also set \a font on all already existing legend items.
13196 \see setFont, QCPAbstractLegendItem::setSelectedFont
13198 void QCPLegend::setSelectedFont(const QFont &font)
13200 mSelectedFont = font;
13201 for (int i=0; i<itemCount(); ++i)
13204 item(i)->setSelectedFont(font);
13209 Sets the default text color that is used by legend items when they are selected.
13211 This function will also set \a color on all already existing legend items.
13213 \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
13215 void QCPLegend::setSelectedTextColor(const QColor &color)
13217 mSelectedTextColor = color;
13218 for (int i=0; i<itemCount(); ++i)
13221 item(i)->setSelectedTextColor(color);
13226 Returns the item with index \a i.
13230 QCPAbstractLegendItem *QCPLegend::item(int index) const
13232 return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13236 Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13237 If such an item isn't in the legend, returns 0.
13239 \see hasItemWithPlottable
13241 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
13243 for (int i=0; i<itemCount(); ++i)
13245 if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13247 if (pli->plottable() == plottable)
13255 Returns the number of items currently in the legend.
13258 int QCPLegend::itemCount() const
13260 return elementCount();
13264 Returns whether the legend contains \a itm.
13266 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
13268 for (int i=0; i<itemCount(); ++i)
13270 if (item == this->item(i))
13277 Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13278 If such an item isn't in the legend, returns false.
13280 \see itemWithPlottable
13282 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
13284 return itemWithPlottable(plottable);
13288 Adds \a item to the legend, if it's not present already.
13290 Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
13292 The legend takes ownership of the item.
13294 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
13296 if (!hasItem(item))
13298 return addElement(rowCount(), 0, item);
13304 Removes the item with index \a index from the legend.
13306 Returns true, if successful.
13308 \see itemCount, clearItems
13310 bool QCPLegend::removeItem(int index)
13312 if (QCPAbstractLegendItem *ali = item(index))
13314 bool success = remove(ali);
13323 Removes \a item from the legend.
13325 Returns true, if successful.
13329 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
13331 bool success = remove(item);
13337 Removes all items from the legend.
13339 void QCPLegend::clearItems()
13341 for (int i=itemCount()-1; i>=0; --i)
13346 Returns the legend items that are currently selected. If no items are selected,
13349 \see QCPAbstractLegendItem::setSelected, setSelectable
13351 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13353 QList<QCPAbstractLegendItem*> result;
13354 for (int i=0; i<itemCount(); ++i)
13356 if (QCPAbstractLegendItem *ali = item(i))
13358 if (ali->selected())
13359 result.append(ali);
13367 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
13368 before drawing main legend elements.
13370 This is the antialiasing state the painter passed to the \ref draw method is in by default.
13372 This function takes into account the local setting of the antialiasing flag as well as the
13373 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
13374 QCustomPlot::setNotAntialiasedElements.
13376 \see setAntialiased
13378 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
13380 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
13385 Returns the pen used to paint the border of the legend, taking into account the selection state
13388 QPen QCPLegend::getBorderPen() const
13390 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13395 Returns the brush used to paint the background of the legend, taking into account the selection
13396 state of the legend box.
13398 QBrush QCPLegend::getBrush() const
13400 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13405 Draws the legend box with the provided \a painter. The individual legend items are layerables
13406 themselves, thus are drawn independently.
13408 void QCPLegend::draw(QCPPainter *painter)
13410 // draw background rect:
13411 painter->setBrush(getBrush());
13412 painter->setPen(getBorderPen());
13413 painter->drawRect(mOuterRect);
13416 /* inherits documentation from base class */
13417 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13419 if (!mParentPlot) return -1;
13420 if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13423 if (mOuterRect.contains(pos.toPoint()))
13425 if (details) details->setValue(spLegendBox);
13426 return mParentPlot->selectionTolerance()*0.99;
13431 /* inherits documentation from base class */
13432 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13435 mSelectedParts = selectedParts(); // in case item selection has changed
13436 if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13438 SelectableParts selBefore = mSelectedParts;
13439 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)
13440 if (selectionStateChanged)
13441 *selectionStateChanged = mSelectedParts != selBefore;
13445 /* inherits documentation from base class */
13446 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13448 mSelectedParts = selectedParts(); // in case item selection has changed
13449 if (mSelectableParts.testFlag(spLegendBox))
13451 SelectableParts selBefore = mSelectedParts;
13452 setSelectedParts(selectedParts() & ~spLegendBox);
13453 if (selectionStateChanged)
13454 *selectionStateChanged = mSelectedParts != selBefore;
13458 /* inherits documentation from base class */
13459 QCP::Interaction QCPLegend::selectionCategory() const
13461 return QCP::iSelectLegend;
13464 /* inherits documentation from base class */
13465 QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
13467 return QCP::iSelectLegend;
13470 /* inherits documentation from base class */
13471 void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
13473 Q_UNUSED(parentPlot)
13477 ////////////////////////////////////////////////////////////////////////////////////////////////////
13478 //////////////////// QCPPlotTitle
13479 ////////////////////////////////////////////////////////////////////////////////////////////////////
13481 /*! \class QCPPlotTitle
13482 \brief A layout element displaying a plot title text
13484 The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
13485 and \ref setTextColor.
13487 A plot title can be added as follows:
13488 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation
13490 Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
13491 easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
13492 signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
13493 QCustomPlot::titleDoubleClick signal.
13496 /* start documentation of signals */
13498 /*! \fn void QCPPlotTitle::selectionChanged(bool selected)
13500 This signal is emitted when the selection state has changed to \a selected, either by user
13501 interaction or by a direct call to \ref setSelected.
13503 \see setSelected, setSelectable
13506 /* end documentation of signals */
13509 Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
13511 To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
13513 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
13514 QCPLayoutElement(parentPlot),
13515 mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
13516 mTextColor(Qt::black),
13517 mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
13518 mSelectedTextColor(Qt::blue),
13519 mSelectable(false),
13524 setLayer(parentPlot->currentLayer());
13525 mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13526 mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13528 setMargins(QMargins(5, 5, 5, 0));
13533 Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
13535 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13536 QCPLayoutElement(parentPlot),
13538 mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13539 mTextColor(Qt::black),
13540 mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13541 mSelectedTextColor(Qt::blue),
13542 mSelectable(false),
13545 setLayer(QLatin1String("axes"));
13546 setMargins(QMargins(5, 5, 5, 0));
13550 Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
13552 \see setFont, setTextColor
13554 void QCPPlotTitle::setText(const QString &text)
13560 Sets the \a font of the title text.
13562 \see setTextColor, setSelectedFont
13564 void QCPPlotTitle::setFont(const QFont &font)
13570 Sets the \a color of the title text.
13572 \see setFont, setSelectedTextColor
13574 void QCPPlotTitle::setTextColor(const QColor &color)
13576 mTextColor = color;
13580 Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
13584 void QCPPlotTitle::setSelectedFont(const QFont &font)
13586 mSelectedFont = font;
13590 Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
13594 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13596 mSelectedTextColor = color;
13600 Sets whether the user may select this plot title to \a selectable.
13602 Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
13603 programmatically via \ref setSelected.
13605 void QCPPlotTitle::setSelectable(bool selectable)
13607 if (mSelectable != selectable)
13609 mSelectable = selectable;
13610 emit selectableChanged(mSelectable);
13615 Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
13616 selectionChanged is emitted.
13618 Note that this function can change the selection state independently of the current \ref
13619 setSelectable state.
13621 void QCPPlotTitle::setSelected(bool selected)
13623 if (mSelected != selected)
13625 mSelected = selected;
13626 emit selectionChanged(mSelected);
13630 /* inherits documentation from base class */
13631 void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
13633 applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
13636 /* inherits documentation from base class */
13637 void QCPPlotTitle::draw(QCPPainter *painter)
13639 painter->setFont(mainFont());
13640 painter->setPen(QPen(mainTextColor()));
13641 painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13644 /* inherits documentation from base class */
13645 QSize QCPPlotTitle::minimumSizeHint() const
13647 QFontMetrics metrics(mFont);
13648 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13649 result.rwidth() += mMargins.left() + mMargins.right();
13650 result.rheight() += mMargins.top() + mMargins.bottom();
13654 /* inherits documentation from base class */
13655 QSize QCPPlotTitle::maximumSizeHint() const
13657 QFontMetrics metrics(mFont);
13658 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13659 result.rheight() += mMargins.top() + mMargins.bottom();
13660 result.setWidth(QWIDGETSIZE_MAX);
13664 /* inherits documentation from base class */
13665 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13671 bool selBefore = mSelected;
13672 setSelected(additive ? !mSelected : true);
13673 if (selectionStateChanged)
13674 *selectionStateChanged = mSelected != selBefore;
13678 /* inherits documentation from base class */
13679 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13683 bool selBefore = mSelected;
13684 setSelected(false);
13685 if (selectionStateChanged)
13686 *selectionStateChanged = mSelected != selBefore;
13690 /* inherits documentation from base class */
13691 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13694 if (onlySelectable && !mSelectable)
13697 if (mTextBoundingRect.contains(pos.toPoint()))
13698 return mParentPlot->selectionTolerance()*0.99;
13705 Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
13706 <tt>true</tt>, else mFont is returned.
13708 QFont QCPPlotTitle::mainFont() const
13710 return mSelected ? mSelectedFont : mFont;
13715 Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
13716 <tt>true</tt>, else mTextColor is returned.
13718 QColor QCPPlotTitle::mainTextColor() const
13720 return mSelected ? mSelectedTextColor : mTextColor;
13724 ////////////////////////////////////////////////////////////////////////////////////////////////////
13725 //////////////////// QCPColorScale
13726 ////////////////////////////////////////////////////////////////////////////////////////////////////
13728 /*! \class QCPColorScale
13729 \brief A color scale for use with color coding data such as QCPColorMap
13731 This layout element can be placed on the plot to correlate a color gradient with data values. It
13732 is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
13734 \image html QCPColorScale.png
13736 The color scale can be either horizontal or vertical, as shown in the image above. The
13737 orientation and the side where the numbers appear is controlled with \ref setType.
13739 Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
13740 connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
13741 setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
13742 scale, to make them all synchronize these properties.
13744 To have finer control over the number display and axis behaviour, you can directly access the
13745 \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
13746 you want to change the number of automatically generated ticks, call
13747 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount
13749 Placing a color scale next to the main axis rect works like with any other layout element:
13750 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation
13751 In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
13752 call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
13753 scale can be set with \ref setLabel.
13755 For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
13756 the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
13757 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup
13759 Color scales are initialized with a non-zero minimum top and bottom margin (\ref
13760 setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
13761 margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
13762 horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
13763 might want to also change the minimum margins accordingly, e.g. <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.
13766 /* start documentation of inline functions */
13768 /*! \fn QCPAxis *QCPColorScale::axis() const
13770 Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
13771 appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
13772 interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
13773 setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
13774 QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
13775 the QCPColorScale or on its QCPAxis.
13777 If the type of the color scale is changed with \ref setType, the axis returned by this method
13778 will change, too, to either the left, right, bottom or top axis, depending on which type was set.
13781 /* end documentation of signals */
13782 /* start documentation of signals */
13784 /*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
13786 This signal is emitted when the data range changes.
13791 /*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
13793 This signal is emitted when the data scale type changes.
13795 \see setDataScaleType
13798 /*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
13800 This signal is emitted when the gradient changes.
13805 /* end documentation of signals */
13808 Constructs a new QCPColorScale.
13810 QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
13811 QCPLayoutElement(parentPlot),
13812 mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13813 mDataScaleType(QCPAxis::stLinear),
13815 mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13817 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)
13818 setType(QCPAxis::atRight);
13819 setDataRange(QCPRange(0, 6));
13822 QCPColorScale::~QCPColorScale()
13827 /* undocumented getter */
13828 QString QCPColorScale::label() const
13832 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13836 return mColorAxis.data()->label();
13839 /* undocumented getter */
13840 bool QCPColorScale::rangeDrag() const
13844 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13848 return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13849 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13850 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13853 /* undocumented getter */
13854 bool QCPColorScale::rangeZoom() const
13858 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13862 return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13863 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13864 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13868 Sets at which side of the color scale the axis is placed, and thus also its orientation.
13870 Note that after setting \a type to a different value, the axis returned by \ref axis() will
13871 be a different one. The new axis will adopt the following properties from the previous axis: The
13872 range, scale type, log base and label.
13874 void QCPColorScale::setType(QCPAxis::AxisType type)
13878 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13884 QCPRange rangeTransfer(0, 6);
13885 double logBaseTransfer = 10;
13886 QString labelTransfer;
13887 // revert some settings on old axis:
13890 rangeTransfer = mColorAxis.data()->range();
13891 labelTransfer = mColorAxis.data()->label();
13892 logBaseTransfer = mColorAxis.data()->scaleLogBase();
13893 mColorAxis.data()->setLabel(QString());
13894 disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13895 disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13897 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
13898 foreach (QCPAxis::AxisType atype, allAxisTypes)
13900 mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13901 mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13903 // set new mColorAxis pointer:
13904 mColorAxis = mAxisRect.data()->axis(mType);
13905 // transfer settings to new axis:
13906 mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13907 mColorAxis.data()->setLabel(labelTransfer);
13908 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
13909 connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13910 connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13911 mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13912 QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13917 Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
13919 It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
13920 also equivalent to directly accessing the \ref axis and setting its range with \ref
13923 \see setDataScaleType, setGradient, rescaleDataRange
13925 void QCPColorScale::setDataRange(const QCPRange &dataRange)
13927 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13929 mDataRange = dataRange;
13931 mColorAxis.data()->setRange(mDataRange);
13932 emit dataRangeChanged(mDataRange);
13937 Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
13938 or logarithmically.
13940 It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
13941 also equivalent to directly accessing the \ref axis and setting its scale type with \ref
13942 QCPAxis::setScaleType.
13944 \see setDataRange, setGradient
13946 void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
13948 if (mDataScaleType != scaleType)
13950 mDataScaleType = scaleType;
13952 mColorAxis.data()->setScaleType(mDataScaleType);
13953 if (mDataScaleType == QCPAxis::stLogarithmic)
13954 setDataRange(mDataRange.sanitizedForLogScale());
13955 emit dataScaleTypeChanged(mDataScaleType);
13960 Sets the color gradient that will be used to represent data values.
13962 It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
13964 \see setDataRange, setDataScaleType
13966 void QCPColorScale::setGradient(const QCPColorGradient &gradient)
13968 if (mGradient != gradient)
13970 mGradient = gradient;
13972 mAxisRect.data()->mGradientImageInvalidated = true;
13973 emit gradientChanged(mGradient);
13978 Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
13979 the internal \ref axis.
13981 void QCPColorScale::setLabel(const QString &str)
13985 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13989 mColorAxis.data()->setLabel(str);
13993 Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
13996 void QCPColorScale::setBarWidth(int width)
14002 Sets whether the user can drag the data range (\ref setDataRange).
14004 Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
14005 QCustomPlot::setInteractions) to allow range dragging.
14007 void QCPColorScale::setRangeDrag(bool enabled)
14011 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14016 mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14018 mAxisRect.data()->setRangeDrag(0);
14022 Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
14024 Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
14025 QCustomPlot::setInteractions) to allow range dragging.
14027 void QCPColorScale::setRangeZoom(bool enabled)
14031 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14036 mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14038 mAxisRect.data()->setRangeZoom(0);
14042 Returns a list of all the color maps associated with this color scale.
14044 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14046 QList<QCPColorMap*> result;
14047 for (int i=0; i<mParentPlot->plottableCount(); ++i)
14049 if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14050 if (cm->colorScale() == this)
14057 Changes the data range such that all color maps associated with this color scale are fully mapped
14058 to the gradient in the data dimension.
14062 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14064 QList<QCPColorMap*> maps = colorMaps();
14066 bool haveRange = false;
14067 int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14068 if (mDataScaleType == QCPAxis::stLogarithmic)
14069 sign = (mDataRange.upper < 0 ? -1 : 1);
14070 for (int i=0; i<maps.size(); ++i)
14072 if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14075 if (maps.at(i)->colorScale() == this)
14077 bool currentFoundRange = true;
14078 mapRange = maps.at(i)->data()->dataBounds();
14081 if (mapRange.lower <= 0 && mapRange.upper > 0)
14082 mapRange.lower = mapRange.upper*1e-3;
14083 else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14084 currentFoundRange = false;
14085 } else if (sign == -1)
14087 if (mapRange.upper >= 0 && mapRange.lower < 0)
14088 mapRange.upper = mapRange.lower*1e-3;
14089 else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14090 currentFoundRange = false;
14092 if (currentFoundRange)
14095 newRange = mapRange;
14097 newRange.expand(mapRange);
14104 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
14106 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
14107 if (mDataScaleType == QCPAxis::stLinear)
14109 newRange.lower = center-mDataRange.size()/2.0;
14110 newRange.upper = center+mDataRange.size()/2.0;
14111 } else // mScaleType == stLogarithmic
14113 newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14114 newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14117 setDataRange(newRange);
14121 /* inherits documentation from base class */
14122 void QCPColorScale::update(UpdatePhase phase)
14124 QCPLayoutElement::update(phase);
14127 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14131 mAxisRect.data()->update(phase);
14137 if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
14139 setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14140 setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14143 setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14144 setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14150 mAxisRect.data()->setOuterRect(rect());
14157 /* inherits documentation from base class */
14158 void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
14160 painter->setAntialiasing(false);
14163 /* inherits documentation from base class */
14164 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14168 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14171 mAxisRect.data()->mousePressEvent(event);
14174 /* inherits documentation from base class */
14175 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14179 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14182 mAxisRect.data()->mouseMoveEvent(event);
14185 /* inherits documentation from base class */
14186 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14190 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14193 mAxisRect.data()->mouseReleaseEvent(event);
14196 /* inherits documentation from base class */
14197 void QCPColorScale::wheelEvent(QWheelEvent *event)
14201 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14204 mAxisRect.data()->wheelEvent(event);
14207 ////////////////////////////////////////////////////////////////////////////////////////////////////
14208 //////////////////// QCPColorScaleAxisRectPrivate
14209 ////////////////////////////////////////////////////////////////////////////////////////////////////
14211 /*! \class QCPColorScaleAxisRectPrivate
14214 \brief An axis rect subclass for use in a QCPColorScale
14216 This is a private class and not part of the public QCustomPlot interface.
14218 It provides the axis rect functionality for the QCPColorScale class.
14223 Creates a new instance, as a child of \a parentColorScale.
14225 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
14226 QCPAxisRect(parentColorScale->parentPlot(), true),
14227 mParentColorScale(parentColorScale),
14228 mGradientImageInvalidated(true)
14230 setParentLayerable(parentColorScale);
14231 setMinimumMargins(QMargins(0, 0, 0, 0));
14232 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14233 foreach (QCPAxis::AxisType type, allAxisTypes)
14235 axis(type)->setVisible(true);
14236 axis(type)->grid()->setVisible(false);
14237 axis(type)->setPadding(0);
14238 connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14239 connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14242 connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14243 connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14244 connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14245 connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14246 connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14247 connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14248 connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14249 connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14251 // make layer transfers of color scale transfer to axis rect and axes
14252 // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14253 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14254 foreach (QCPAxis::AxisType type, allAxisTypes)
14255 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14259 Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
14260 it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
14262 void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
14264 if (mGradientImageInvalidated)
14265 updateGradientImage();
14267 bool mirrorHorz = false;
14268 bool mirrorVert = false;
14269 if (mParentColorScale->mColorAxis)
14271 mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14272 mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14275 painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14276 QCPAxisRect::draw(painter);
14281 Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
14282 generate a gradient image. This gradient image will be used in the \ref draw method.
14284 void QCPColorScaleAxisRectPrivate::updateGradientImage()
14286 if (rect().isEmpty())
14289 int n = mParentColorScale->mGradient.levelCount();
14291 QVector<double> data(n);
14292 for (int i=0; i<n; ++i)
14294 if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
14297 h = rect().height();
14298 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14299 QVector<QRgb*> pixels;
14300 for (int y=0; y<h; ++y)
14301 pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14302 mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14303 for (int y=1; y<h; ++y)
14304 memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14307 w = rect().width();
14309 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14310 for (int y=0; y<h; ++y)
14312 QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14313 const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14314 for (int x=0; x<w; ++x)
14315 pixels[x] = lineColor;
14318 mGradientImageInvalidated = false;
14323 This slot is connected to the selectionChanged signals of the four axes in the constructor. It
14324 synchronizes the selection state of the axes.
14326 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14328 // axis bases of four axes shall always (de-)selected synchronously:
14329 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14330 foreach (QCPAxis::AxisType type, allAxisTypes)
14332 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14333 if (senderAxis->axisType() == type)
14336 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14338 if (selectedParts.testFlag(QCPAxis::spAxis))
14339 axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14341 axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14348 This slot is connected to the selectableChanged signals of the four axes in the constructor. It
14349 synchronizes the selectability of the axes.
14351 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14353 // synchronize axis base selectability:
14354 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14355 foreach (QCPAxis::AxisType type, allAxisTypes)
14357 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14358 if (senderAxis->axisType() == type)
14361 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14363 if (selectableParts.testFlag(QCPAxis::spAxis))
14364 axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14366 axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14372 ////////////////////////////////////////////////////////////////////////////////////////////////////
14373 //////////////////// QCPData
14374 ////////////////////////////////////////////////////////////////////////////////////////////////////
14377 \brief Holds the data of one single data point for QCPGraph.
14379 The container for storing multiple data points is \ref QCPDataMap.
14381 The stored data is:
14382 \li \a key: coordinate on the key axis of this data point
14383 \li \a value: coordinate on the value axis of this data point
14384 \li \a keyErrorMinus: negative error in the key dimension (for error bars)
14385 \li \a keyErrorPlus: positive error in the key dimension (for error bars)
14386 \li \a valueErrorMinus: negative error in the value dimension (for error bars)
14387 \li \a valueErrorPlus: positive error in the value dimension (for error bars)
14393 Constructs a data point with key, value and all errors set to zero.
14395 QCPData::QCPData() :
14406 Constructs a data point with the specified \a key and \a value. All errors are set to zero.
14408 QCPData::QCPData(double key, double value) :
14419 ////////////////////////////////////////////////////////////////////////////////////////////////////
14420 //////////////////// QCPGraph
14421 ////////////////////////////////////////////////////////////////////////////////////////////////////
14423 /*! \class QCPGraph
14424 \brief A plottable representing a graph in a plot.
14426 \image html QCPGraph.png
14428 Usually you create new graphs by calling QCustomPlot::addGraph. The resulting instance can be
14429 accessed via QCustomPlot::graph.
14431 To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
14432 also access and modify the graph's data via the \ref data method, which returns a pointer to the
14433 internal \ref QCPDataMap.
14435 Graphs are used to display single-valued data. Single-valued means that there should only be one
14436 data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
14437 want to plot non-single-valued curves, rather use the QCPCurve plottable.
14439 Gaps in the graph line can be created by adding data points with NaN as value
14440 (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
14443 \section appearance Changing the appearance
14445 The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
14446 of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
14448 \subsection filling Filling under or between graphs
14450 QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
14451 the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
14452 just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
14454 By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
14455 between this graph and another one, call \ref setChannelFillGraph with the other graph as
14458 \see QCustomPlot::addGraph, QCustomPlot::graph
14461 /* start of documentation of inline functions */
14463 /*! \fn QCPDataMap *QCPGraph::data() const
14465 Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
14466 directly manipulate the data, which may be more convenient and faster than using the regular \ref
14467 setData or \ref addData methods, in certain situations.
14470 /* end of documentation of inline functions */
14473 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
14474 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
14475 the same orientation. If either of these restrictions is violated, a corresponding message is
14476 printed to the debug output (qDebug), the construction is not aborted, though.
14478 The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
14479 then takes ownership of the graph.
14481 To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
14483 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14484 QCPAbstractPlottable(keyAxis, valueAxis)
14486 mData = new QCPDataMap;
14488 setPen(QPen(Qt::blue, 0));
14489 setErrorPen(QPen(Qt::black));
14490 setBrush(Qt::NoBrush);
14491 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14492 setSelectedBrush(Qt::NoBrush);
14494 setLineStyle(lsLine);
14495 setErrorType(etNone);
14496 setErrorBarSize(6);
14497 setErrorBarSkipSymbol(true);
14498 setChannelFillGraph(0);
14499 setAdaptiveSampling(true);
14502 QCPGraph::~QCPGraph()
14508 Replaces the current data with the provided \a data.
14510 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
14511 takes ownership of the passed data and replaces the internal data pointer with it. This is
14512 significantly faster than copying for large datasets.
14514 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14515 returns a pointer to the internal \ref QCPDataMap.
14517 void QCPGraph::setData(QCPDataMap *data, bool copy)
14521 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
14536 Replaces the current data with the provided points in \a key and \a value pairs. The provided
14537 vectors should have equal length. Else, the number of added points will be the size of the
14540 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14543 int n = key.size();
14544 n = qMin(n, value.size());
14546 for (int i=0; i<n; ++i)
14548 newData.key = key[i];
14549 newData.value = value[i];
14550 mData->insertMulti(newData.key, newData);
14555 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14556 symmetrical value error of the data points are set to the values in \a valueError.
14557 For error bars to show appropriately, see \ref setErrorType.
14558 The provided vectors should have equal length. Else, the number of added points will be the size of the
14561 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14563 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14566 int n = key.size();
14567 n = qMin(n, value.size());
14568 n = qMin(n, valueError.size());
14570 for (int i=0; i<n; ++i)
14572 newData.key = key[i];
14573 newData.value = value[i];
14574 newData.valueErrorMinus = valueError[i];
14575 newData.valueErrorPlus = valueError[i];
14576 mData->insertMulti(key[i], newData);
14582 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14583 negative value error of the data points are set to the values in \a valueErrorMinus, the positive
14584 value error to \a valueErrorPlus.
14585 For error bars to show appropriately, see \ref setErrorType.
14586 The provided vectors should have equal length. Else, the number of added points will be the size of the
14589 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14592 int n = key.size();
14593 n = qMin(n, value.size());
14594 n = qMin(n, valueErrorMinus.size());
14595 n = qMin(n, valueErrorPlus.size());
14597 for (int i=0; i<n; ++i)
14599 newData.key = key[i];
14600 newData.value = value[i];
14601 newData.valueErrorMinus = valueErrorMinus[i];
14602 newData.valueErrorPlus = valueErrorPlus[i];
14603 mData->insertMulti(key[i], newData);
14608 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14609 symmetrical key error of the data points are set to the values in \a keyError.
14610 For error bars to show appropriately, see \ref setErrorType.
14611 The provided vectors should have equal length. Else, the number of added points will be the size of the
14614 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14616 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14619 int n = key.size();
14620 n = qMin(n, value.size());
14621 n = qMin(n, keyError.size());
14623 for (int i=0; i<n; ++i)
14625 newData.key = key[i];
14626 newData.value = value[i];
14627 newData.keyErrorMinus = keyError[i];
14628 newData.keyErrorPlus = keyError[i];
14629 mData->insertMulti(key[i], newData);
14635 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14636 negative key error of the data points are set to the values in \a keyErrorMinus, the positive
14637 key error to \a keyErrorPlus.
14638 For error bars to show appropriately, see \ref setErrorType.
14639 The provided vectors should have equal length. Else, the number of added points will be the size of the
14642 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14645 int n = key.size();
14646 n = qMin(n, value.size());
14647 n = qMin(n, keyErrorMinus.size());
14648 n = qMin(n, keyErrorPlus.size());
14650 for (int i=0; i<n; ++i)
14652 newData.key = key[i];
14653 newData.value = value[i];
14654 newData.keyErrorMinus = keyErrorMinus[i];
14655 newData.keyErrorPlus = keyErrorPlus[i];
14656 mData->insertMulti(key[i], newData);
14661 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14662 symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
14663 For error bars to show appropriately, see \ref setErrorType.
14664 The provided vectors should have equal length. Else, the number of added points will be the size of the
14667 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14669 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14672 int n = key.size();
14673 n = qMin(n, value.size());
14674 n = qMin(n, valueError.size());
14675 n = qMin(n, keyError.size());
14677 for (int i=0; i<n; ++i)
14679 newData.key = key[i];
14680 newData.value = value[i];
14681 newData.keyErrorMinus = keyError[i];
14682 newData.keyErrorPlus = keyError[i];
14683 newData.valueErrorMinus = valueError[i];
14684 newData.valueErrorPlus = valueError[i];
14685 mData->insertMulti(key[i], newData);
14691 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14692 negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
14693 key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
14694 For error bars to show appropriately, see \ref setErrorType.
14695 The provided vectors should have equal length. Else, the number of added points will be the size of the
14698 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)
14701 int n = key.size();
14702 n = qMin(n, value.size());
14703 n = qMin(n, valueErrorMinus.size());
14704 n = qMin(n, valueErrorPlus.size());
14705 n = qMin(n, keyErrorMinus.size());
14706 n = qMin(n, keyErrorPlus.size());
14708 for (int i=0; i<n; ++i)
14710 newData.key = key[i];
14711 newData.value = value[i];
14712 newData.keyErrorMinus = keyErrorMinus[i];
14713 newData.keyErrorPlus = keyErrorPlus[i];
14714 newData.valueErrorMinus = valueErrorMinus[i];
14715 newData.valueErrorPlus = valueErrorPlus[i];
14716 mData->insertMulti(key[i], newData);
14722 Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to
14723 \ref lsNone and \ref setScatterStyle to the desired scatter style.
14725 \see setScatterStyle
14727 void QCPGraph::setLineStyle(LineStyle ls)
14733 Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
14734 are drawn (e.g. for line-only-plots with appropriate line style).
14736 \see QCPScatterStyle, setLineStyle
14738 void QCPGraph::setScatterStyle(const QCPScatterStyle &style)
14740 mScatterStyle = style;
14744 Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
14745 point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
14746 error data via the specific setData functions along with the data points (e.g. \ref
14747 setDataValueError, \ref setDataKeyError, \ref setDataBothError).
14751 void QCPGraph::setErrorType(ErrorType errorType)
14753 mErrorType = errorType;
14757 Sets the pen with which the error bars will be drawn.
14758 \see setErrorBarSize, setErrorType
14760 void QCPGraph::setErrorPen(const QPen &pen)
14766 Sets the width of the handles at both ends of an error bar in pixels.
14768 void QCPGraph::setErrorBarSize(double size)
14770 mErrorBarSize = size;
14774 If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
14775 leave some free space around the symbol.
14777 This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to determine the size
14778 of the area to leave blank. So when drawing Pixmaps as scatter points (\ref
14779 QCPScatterStyle::ssPixmap), the scatter size must be set manually to a value corresponding to the
14780 size of the Pixmap, if the error bars should leave gaps to its boundaries.
14782 \ref setErrorType, setErrorBarSize, setScatterStyle
14784 void QCPGraph::setErrorBarSkipSymbol(bool enabled)
14786 mErrorBarSkipSymbol = enabled;
14790 Sets the target graph for filling the area between this graph and \a targetGraph with the current
14791 brush (\ref setBrush).
14793 When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To
14794 disable any filling, set the brush to Qt::NoBrush.
14798 void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
14800 // prevent setting channel target to this graph itself:
14801 if (targetGraph == this)
14803 qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14804 mChannelFillGraph = 0;
14807 // prevent setting channel target to a graph not in the plot:
14808 if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14810 qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14811 mChannelFillGraph = 0;
14815 mChannelFillGraph = targetGraph;
14819 Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
14820 sampling technique can drastically improve the replot performance for graphs with a larger number
14821 of points (e.g. above 10,000), without notably changing the appearance of the graph.
14823 By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
14824 sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
14825 disadvantage in almost all cases.
14827 \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
14829 As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
14830 reproduced reliably, as well as the overall shape of the data set. The replot time reduces
14831 dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
14833 \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
14835 Care must be taken when using high-density scatter plots in combination with adaptive sampling.
14836 The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
14837 gives a significant reduction of replot times, but not quite as much as for line plots. This is
14838 because scatter plots inherently need more data points to be preserved in order to still resemble
14839 the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
14840 identical, as banding occurs for the outer data points. This is in fact intentional, such that
14841 the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
14842 depends on the point density, i.e. the number of points in the plot.
14844 For some situations with scatter plots it might thus be desirable to manually turn adaptive
14845 sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
14846 enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
14847 back to true afterwards.
14849 void QCPGraph::setAdaptiveSampling(bool enabled)
14851 mAdaptiveSampling = enabled;
14855 Adds the provided data points in \a dataMap to the current data.
14857 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14858 returns a pointer to the internal \ref QCPDataMap.
14862 void QCPGraph::addData(const QCPDataMap &dataMap)
14864 mData->unite(dataMap);
14868 Adds the provided single data point in \a data to the current data.
14870 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14871 returns a pointer to the internal \ref QCPDataMap.
14875 void QCPGraph::addData(const QCPData &data)
14877 mData->insertMulti(data.key, data);
14881 Adds the provided single data point as \a key and \a value pair to the current data.
14883 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14884 returns a pointer to the internal \ref QCPDataMap.
14888 void QCPGraph::addData(double key, double value)
14892 newData.value = value;
14893 mData->insertMulti(newData.key, newData);
14897 Adds the provided data points as \a key and \a value pairs to the current data.
14899 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14900 returns a pointer to the internal \ref QCPDataMap.
14904 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14906 int n = qMin(keys.size(), values.size());
14908 for (int i=0; i<n; ++i)
14910 newData.key = keys[i];
14911 newData.value = values[i];
14912 mData->insertMulti(newData.key, newData);
14917 Removes all data points with keys smaller than \a key.
14918 \see addData, clearData
14920 void QCPGraph::removeDataBefore(double key)
14922 QCPDataMap::iterator it = mData->begin();
14923 while (it != mData->end() && it.key() < key)
14924 it = mData->erase(it);
14928 Removes all data points with keys greater than \a key.
14929 \see addData, clearData
14931 void QCPGraph::removeDataAfter(double key)
14933 if (mData->isEmpty()) return;
14934 QCPDataMap::iterator it = mData->upperBound(key);
14935 while (it != mData->end())
14936 it = mData->erase(it);
14940 Removes all data points with keys between \a fromKey and \a toKey.
14941 if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
14942 a single data point with known key, use \ref removeData(double key).
14944 \see addData, clearData
14946 void QCPGraph::removeData(double fromKey, double toKey)
14948 if (fromKey >= toKey || mData->isEmpty()) return;
14949 QCPDataMap::iterator it = mData->upperBound(fromKey);
14950 QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14951 while (it != itEnd)
14952 it = mData->erase(it);
14957 Removes a single data point at \a key. If the position is not known with absolute precision,
14958 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
14959 the suspected position, depeding on the precision with which the key is known.
14961 \see addData, clearData
14963 void QCPGraph::removeData(double key)
14965 mData->remove(key);
14969 Removes all data points.
14970 \see removeData, removeDataAfter, removeDataBefore
14972 void QCPGraph::clearData()
14977 /* inherits documentation from base class */
14978 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14981 if ((onlySelectable && !mSelectable) || mData->isEmpty())
14983 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14985 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
14986 return pointDistance(pos);
14993 Allows to define whether error bars are taken into consideration when determining the new axis
14996 \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
14998 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
15000 rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15001 rescaleValueAxis(onlyEnlarge, includeErrorBars);
15006 Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
15007 when determining the new axis range.
15009 \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis
15011 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15013 // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15014 // that getKeyRange is passed the includeErrorBars value.
15015 if (mData->isEmpty()) return;
15017 QCPAxis *keyAxis = mKeyAxis.data();
15018 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15020 SignDomain signDomain = sdBoth;
15021 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15022 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15025 QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15031 if (keyAxis->range().lower < newRange.lower)
15032 newRange.lower = keyAxis->range().lower;
15033 if (keyAxis->range().upper > newRange.upper)
15034 newRange.upper = keyAxis->range().upper;
15036 keyAxis->setRange(newRange);
15042 Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
15043 when determining the new axis range.
15045 \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis
15047 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15049 // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15050 // is that getValueRange is passed the includeErrorBars value.
15051 if (mData->isEmpty()) return;
15053 QCPAxis *valueAxis = mValueAxis.data();
15054 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15056 SignDomain signDomain = sdBoth;
15057 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15058 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15061 QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15067 if (valueAxis->range().lower < newRange.lower)
15068 newRange.lower = valueAxis->range().lower;
15069 if (valueAxis->range().upper > newRange.upper)
15070 newRange.upper = valueAxis->range().upper;
15072 valueAxis->setRange(newRange);
15076 /* inherits documentation from base class */
15077 void QCPGraph::draw(QCPPainter *painter)
15079 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15080 if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15081 if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15083 // allocate line and (if necessary) point vectors:
15084 QVector<QPointF> *lineData = new QVector<QPointF>;
15085 QVector<QCPData> *scatterData = 0;
15086 if (!mScatterStyle.isNone())
15087 scatterData = new QVector<QCPData>;
15089 // fill vectors with data appropriate to plot style:
15090 getPlotData(lineData, scatterData);
15092 // check data validity if flag set:
15093 #ifdef QCUSTOMPLOT_CHECK_DATA
15094 QCPDataMap::const_iterator it;
15095 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15097 if (QCP::isInvalidData(it.value().key, it.value().value) ||
15098 QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15099 QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15100 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15104 // draw fill of graph:
15105 if (mLineStyle != lsNone)
15106 drawFill(painter, lineData);
15109 if (mLineStyle == lsImpulse)
15110 drawImpulsePlot(painter, lineData);
15111 else if (mLineStyle != lsNone)
15112 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15116 drawScatterPlot(painter, scatterData);
15118 // free allocated line and point vectors:
15121 delete scatterData;
15124 /* inherits documentation from base class */
15125 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15128 if (mBrush.style() != Qt::NoBrush)
15130 applyFillAntialiasingHint(painter);
15131 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15133 // draw line vertically centered:
15134 if (mLineStyle != lsNone)
15136 applyDefaultAntialiasingHint(painter);
15137 painter->setPen(mPen);
15138 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
15140 // draw scatter symbol:
15141 if (!mScatterStyle.isNone())
15143 applyScattersAntialiasingHint(painter);
15144 // scale scatter pixmap if it's too large to fit in legend icon rect:
15145 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15147 QCPScatterStyle scaledStyle(mScatterStyle);
15148 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15149 scaledStyle.applyTo(painter, mPen);
15150 scaledStyle.drawShape(painter, QRectF(rect).center());
15153 mScatterStyle.applyTo(painter, mPen);
15154 mScatterStyle.drawShape(painter, QRectF(rect).center());
15161 This function branches out to the line style specific "get(...)PlotData" functions, according to
15162 the line style of the graph.
15164 \a lineData will be filled with raw points that will be drawn with the according draw functions,
15165 e.g. \ref drawLinePlot and \ref drawImpulsePlot. These aren't necessarily the original data
15166 points, since for step plots for example, additional points are needed for drawing lines that
15167 make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
15170 \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
15171 scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
15172 \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
15174 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
15175 getStepCenterPlotData, getImpulsePlotData
15177 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15181 case lsNone: getScatterPlotData(scatterData); break;
15182 case lsLine: getLinePlotData(lineData, scatterData); break;
15183 case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15184 case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15185 case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15186 case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15192 If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
15193 this function serves at providing the visible data points in \a scatterData, so the \ref
15194 drawScatterPlot function can draw the scatter points accordingly.
15196 If line style is not \ref lsNone, this function is not called and the data for the scatter points
15197 are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
15199 \see drawScatterPlot
15201 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15203 getPreparedData(0, scatterData);
15208 Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
15210 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15211 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15212 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15213 scatterData, and the function will skip filling the vector.
15217 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15219 QCPAxis *keyAxis = mKeyAxis.data();
15220 QCPAxis *valueAxis = mValueAxis.data();
15221 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15222 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15224 QVector<QCPData> lineData;
15225 getPreparedData(&lineData, scatterData);
15226 linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15227 linePixelData->resize(lineData.size());
15229 // transform lineData points to pixels:
15230 if (keyAxis->orientation() == Qt::Vertical)
15232 for (int i=0; i<lineData.size(); ++i)
15234 (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15235 (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15237 } else // key axis is horizontal
15239 for (int i=0; i<lineData.size(); ++i)
15241 (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15242 (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15249 Places the raw data points needed for a step plot with left oriented steps in \a lineData.
15251 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15252 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15253 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15254 scatterData, and the function will skip filling the vector.
15258 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15260 QCPAxis *keyAxis = mKeyAxis.data();
15261 QCPAxis *valueAxis = mValueAxis.data();
15262 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15263 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15265 QVector<QCPData> lineData;
15266 getPreparedData(&lineData, scatterData);
15267 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15268 linePixelData->resize(lineData.size()*2);
15270 // calculate steps from lineData and transform to pixel coordinates:
15271 if (keyAxis->orientation() == Qt::Vertical)
15273 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15275 for (int i=0; i<lineData.size(); ++i)
15277 key = keyAxis->coordToPixel(lineData.at(i).key);
15278 (*linePixelData)[i*2+0].setX(lastValue);
15279 (*linePixelData)[i*2+0].setY(key);
15280 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15281 (*linePixelData)[i*2+1].setX(lastValue);
15282 (*linePixelData)[i*2+1].setY(key);
15284 } else // key axis is horizontal
15286 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15288 for (int i=0; i<lineData.size(); ++i)
15290 key = keyAxis->coordToPixel(lineData.at(i).key);
15291 (*linePixelData)[i*2+0].setX(key);
15292 (*linePixelData)[i*2+0].setY(lastValue);
15293 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15294 (*linePixelData)[i*2+1].setX(key);
15295 (*linePixelData)[i*2+1].setY(lastValue);
15302 Places the raw data points needed for a step plot with right oriented steps in \a lineData.
15304 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15305 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15306 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15307 scatterData, and the function will skip filling the vector.
15311 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15313 QCPAxis *keyAxis = mKeyAxis.data();
15314 QCPAxis *valueAxis = mValueAxis.data();
15315 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15316 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15318 QVector<QCPData> lineData;
15319 getPreparedData(&lineData, scatterData);
15320 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15321 linePixelData->resize(lineData.size()*2);
15323 // calculate steps from lineData and transform to pixel coordinates:
15324 if (keyAxis->orientation() == Qt::Vertical)
15326 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15328 for (int i=0; i<lineData.size(); ++i)
15330 value = valueAxis->coordToPixel(lineData.at(i).value);
15331 (*linePixelData)[i*2+0].setX(value);
15332 (*linePixelData)[i*2+0].setY(lastKey);
15333 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15334 (*linePixelData)[i*2+1].setX(value);
15335 (*linePixelData)[i*2+1].setY(lastKey);
15337 } else // key axis is horizontal
15339 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15341 for (int i=0; i<lineData.size(); ++i)
15343 value = valueAxis->coordToPixel(lineData.at(i).value);
15344 (*linePixelData)[i*2+0].setX(lastKey);
15345 (*linePixelData)[i*2+0].setY(value);
15346 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15347 (*linePixelData)[i*2+1].setX(lastKey);
15348 (*linePixelData)[i*2+1].setY(value);
15355 Places the raw data points needed for a step plot with centered steps in \a lineData.
15357 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15358 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15359 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15360 scatterData, and the function will skip filling the vector.
15364 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15366 QCPAxis *keyAxis = mKeyAxis.data();
15367 QCPAxis *valueAxis = mValueAxis.data();
15368 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15369 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15371 QVector<QCPData> lineData;
15372 getPreparedData(&lineData, scatterData);
15373 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15374 linePixelData->resize(lineData.size()*2);
15375 // calculate steps from lineData and transform to pixel coordinates:
15376 if (keyAxis->orientation() == Qt::Vertical)
15378 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15379 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15381 (*linePixelData)[0].setX(lastValue);
15382 (*linePixelData)[0].setY(lastKey);
15383 for (int i=1; i<lineData.size(); ++i)
15385 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15386 (*linePixelData)[i*2-1].setX(lastValue);
15387 (*linePixelData)[i*2-1].setY(key);
15388 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15389 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15390 (*linePixelData)[i*2+0].setX(lastValue);
15391 (*linePixelData)[i*2+0].setY(key);
15393 (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15394 (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15395 } else // key axis is horizontal
15397 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15398 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15400 (*linePixelData)[0].setX(lastKey);
15401 (*linePixelData)[0].setY(lastValue);
15402 for (int i=1; i<lineData.size(); ++i)
15404 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15405 (*linePixelData)[i*2-1].setX(key);
15406 (*linePixelData)[i*2-1].setY(lastValue);
15407 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15408 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15409 (*linePixelData)[i*2+0].setX(key);
15410 (*linePixelData)[i*2+0].setY(lastValue);
15412 (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15413 (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15420 Places the raw data points needed for an impulse plot in \a lineData.
15422 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15423 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15424 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15425 scatterData, and the function will skip filling the vector.
15427 \see drawImpulsePlot
15429 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15431 QCPAxis *keyAxis = mKeyAxis.data();
15432 QCPAxis *valueAxis = mValueAxis.data();
15433 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15434 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15436 QVector<QCPData> lineData;
15437 getPreparedData(&lineData, scatterData);
15438 linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15440 // transform lineData points to pixels:
15441 if (keyAxis->orientation() == Qt::Vertical)
15443 double zeroPointX = valueAxis->coordToPixel(0);
15445 for (int i=0; i<lineData.size(); ++i)
15447 key = keyAxis->coordToPixel(lineData.at(i).key);
15448 (*linePixelData)[i*2+0].setX(zeroPointX);
15449 (*linePixelData)[i*2+0].setY(key);
15450 (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15451 (*linePixelData)[i*2+1].setY(key);
15453 } else // key axis is horizontal
15455 double zeroPointY = valueAxis->coordToPixel(0);
15457 for (int i=0; i<lineData.size(); ++i)
15459 key = keyAxis->coordToPixel(lineData.at(i).key);
15460 (*linePixelData)[i*2+0].setX(key);
15461 (*linePixelData)[i*2+0].setY(zeroPointY);
15462 (*linePixelData)[i*2+1].setX(key);
15463 (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15470 Draws the fill of the graph with the specified brush.
15472 If the fill is a normal fill towards the zero-value-line, only the \a lineData is required (and
15473 two extra points at the zero-value-line, which are added by \ref addFillBasePoints and removed by
15474 \ref removeFillBasePoints after the fill drawing is done).
15476 If the fill is a channel fill between this QCPGraph and another QCPGraph (mChannelFillGraph), the
15477 more complex polygon is calculated with the \ref getChannelFillPolygon function.
15481 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15483 if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15484 if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15486 applyFillAntialiasingHint(painter);
15487 if (!mChannelFillGraph)
15489 // draw base fill under graph, fill goes all the way to the zero-value-line:
15490 addFillBasePoints(lineData);
15491 painter->setPen(Qt::NoPen);
15492 painter->setBrush(mainBrush());
15493 painter->drawPolygon(QPolygonF(*lineData));
15494 removeFillBasePoints(lineData);
15497 // draw channel fill between this graph and mChannelFillGraph:
15498 painter->setPen(Qt::NoPen);
15499 painter->setBrush(mainBrush());
15500 painter->drawPolygon(getChannelFillPolygon(lineData));
15506 Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
15507 of the line style and are always drawn if the scatter style's shape is not \ref
15508 QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
15509 functions, together with the (line style dependent) line data.
15511 \see drawLinePlot, drawImpulsePlot
15513 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15515 QCPAxis *keyAxis = mKeyAxis.data();
15516 QCPAxis *valueAxis = mValueAxis.data();
15517 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15519 // draw error bars:
15520 if (mErrorType != etNone)
15522 applyErrorBarsAntialiasingHint(painter);
15523 painter->setPen(mErrorPen);
15524 if (keyAxis->orientation() == Qt::Vertical)
15526 for (int i=0; i<scatterData->size(); ++i)
15527 drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15530 for (int i=0; i<scatterData->size(); ++i)
15531 drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15535 // draw scatter point symbols:
15536 applyScattersAntialiasingHint(painter);
15537 mScatterStyle.applyTo(painter, mPen);
15538 if (keyAxis->orientation() == Qt::Vertical)
15540 for (int i=0; i<scatterData->size(); ++i)
15541 if (!qIsNaN(scatterData->at(i).value))
15542 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15545 for (int i=0; i<scatterData->size(); ++i)
15546 if (!qIsNaN(scatterData->at(i).value))
15547 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15553 Draws line graphs from the provided data. It connects all points in \a lineData, which was
15554 created by one of the "get(...)PlotData" functions for line styles that require simple line
15555 connections between the point vector they create. These are for example \ref getLinePlotData,
15556 \ref getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
15558 \see drawScatterPlot, drawImpulsePlot
15560 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15562 // draw line of graph:
15563 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15565 applyDefaultAntialiasingHint(painter);
15566 painter->setPen(mainPen());
15567 painter->setBrush(Qt::NoBrush);
15569 /* Draws polyline in batches, currently not used:
15571 while (p < lineData->size())
15573 int batch = qMin(25, lineData->size()-p);
15577 --p; // to draw the connection lines between two batches
15579 painter->drawPolyline(lineData->constData()+p, batch);
15584 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15585 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15586 painter->pen().style() == Qt::SolidLine &&
15587 !painter->modes().testFlag(QCPPainter::pmVectorized) &&
15588 !painter->modes().testFlag(QCPPainter::pmNoCaching))
15591 bool lastIsNan = false;
15592 const int lineDataSize = lineData->size();
15593 while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
15595 ++i; // because drawing works in 1 point retrospect
15596 while (i < lineDataSize)
15598 if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15601 painter->drawLine(lineData->at(i-1), lineData->at(i));
15610 int segmentStart = 0;
15612 const int lineDataSize = lineData->size();
15613 while (i < lineDataSize)
15615 if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()) || qIsInf(lineData->at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block
15617 painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
15618 segmentStart = i+1;
15622 // draw last segment:
15623 painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
15630 Draws impulses from the provided data, i.e. it connects all line pairs in \a lineData, which was
15631 created by \ref getImpulsePlotData.
15633 \see drawScatterPlot, drawLinePlot
15635 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15638 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15640 applyDefaultAntialiasingHint(painter);
15641 QPen pen = mainPen();
15642 pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15643 painter->setPen(pen);
15644 painter->setBrush(Qt::NoBrush);
15645 painter->drawLines(*lineData);
15651 Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into
15652 consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point
15655 0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't
15656 needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a
15657 scatterData should be 0 to prevent unnecessary calculations.
15659 This method is used by the various "get(...)PlotData" methods to get the basic working set of data.
15661 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15663 QCPAxis *keyAxis = mKeyAxis.data();
15664 QCPAxis *valueAxis = mValueAxis.data();
15665 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15666 // get visible data range:
15667 QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15668 getVisibleDataBounds(lower, upper);
15669 if (lower == mData->constEnd() || upper == mData->constEnd())
15672 // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15673 int maxCount = std::numeric_limits<int>::max();
15674 if (mAdaptiveSampling)
15676 int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15677 maxCount = 2*keyPixelSpan+2;
15679 int dataCount = countDataInBounds(lower, upper, maxCount);
15681 if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15685 QCPDataMap::const_iterator it = lower;
15686 QCPDataMap::const_iterator upperEnd = upper+1;
15687 double minValue = it.value().value;
15688 double maxValue = it.value().value;
15689 QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15690 int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15691 int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15692 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15693 double lastIntervalEndKey = currentIntervalStartKey;
15694 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15695 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15696 int intervalDataCount = 1;
15697 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15698 while (it != upperEnd)
15700 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15702 if (it.value().value < minValue)
15703 minValue = it.value().value;
15704 else if (it.value().value > maxValue)
15705 maxValue = it.value().value;
15706 ++intervalDataCount;
15707 } else // new pixel interval started
15709 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15711 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15712 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15713 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15714 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15715 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
15716 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15718 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15719 lastIntervalEndKey = (it-1).value().key;
15720 minValue = it.value().value;
15721 maxValue = it.value().value;
15722 currentIntervalFirstPoint = it;
15723 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15724 if (keyEpsilonVariable)
15725 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15726 intervalDataCount = 1;
15730 // handle last interval:
15731 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15733 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15734 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15735 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15736 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15738 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15743 double valueMaxRange = valueAxis->range().upper;
15744 double valueMinRange = valueAxis->range().lower;
15745 QCPDataMap::const_iterator it = lower;
15746 QCPDataMap::const_iterator upperEnd = upper+1;
15747 double minValue = it.value().value;
15748 double maxValue = it.value().value;
15749 QCPDataMap::const_iterator minValueIt = it;
15750 QCPDataMap::const_iterator maxValueIt = it;
15751 QCPDataMap::const_iterator currentIntervalStart = it;
15752 int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15753 int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15754 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15755 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15756 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15757 int intervalDataCount = 1;
15758 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15759 while (it != upperEnd)
15761 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15763 if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15765 minValue = it.value().value;
15767 } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15769 maxValue = it.value().value;
15772 ++intervalDataCount;
15773 } else // new pixel started
15775 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15777 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15778 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15779 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15780 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15782 while (intervalIt != it)
15784 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15785 scatterData->append(intervalIt.value());
15789 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15790 scatterData->append(currentIntervalStart.value());
15791 minValue = it.value().value;
15792 maxValue = it.value().value;
15793 currentIntervalStart = it;
15794 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15795 if (keyEpsilonVariable)
15796 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15797 intervalDataCount = 1;
15801 // handle last interval:
15802 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15804 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15805 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15806 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15807 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15809 while (intervalIt != it)
15811 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15812 scatterData->append(intervalIt.value());
15816 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15817 scatterData->append(currentIntervalStart.value());
15819 } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15821 QVector<QCPData> *dataVector = 0;
15823 dataVector = lineData;
15824 else if (scatterData)
15825 dataVector = scatterData;
15828 QCPDataMap::const_iterator it = lower;
15829 QCPDataMap::const_iterator upperEnd = upper+1;
15830 dataVector->reserve(dataCount+2); // +2 for possible fill end points
15831 while (it != upperEnd)
15833 dataVector->append(it.value());
15837 if (lineData && scatterData)
15838 *scatterData = *dataVector;
15844 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
15845 point. \a x and \a y pixel positions of the data point are passed since they are already known in
15846 pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
15847 data is therefore only used for the errors, not key and value.
15849 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15851 if (qIsNaN(data.value))
15853 QCPAxis *keyAxis = mKeyAxis.data();
15854 QCPAxis *valueAxis = mValueAxis.data();
15855 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15857 double a, b; // positions of error bar bounds in pixels
15858 double barWidthHalf = mErrorBarSize*0.5;
15859 double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15861 if (keyAxis->orientation() == Qt::Vertical)
15863 // draw key error vertically and value error horizontally
15864 if (mErrorType == etKey || mErrorType == etBoth)
15866 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15867 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15868 if (keyAxis->rangeReversed())
15871 if (mErrorBarSkipSymbol)
15873 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15874 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15875 if (y-b > skipSymbolMargin)
15876 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15878 painter->drawLine(QLineF(x, a, x, b));
15880 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15881 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15883 if (mErrorType == etValue || mErrorType == etBoth)
15885 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15886 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15887 if (valueAxis->rangeReversed())
15890 if (mErrorBarSkipSymbol)
15892 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15893 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15894 if (b-x > skipSymbolMargin)
15895 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15897 painter->drawLine(QLineF(a, y, b, y));
15899 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15900 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15902 } else // mKeyAxis->orientation() is Qt::Horizontal
15904 // draw value error vertically and key error horizontally
15905 if (mErrorType == etKey || mErrorType == etBoth)
15907 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15908 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15909 if (keyAxis->rangeReversed())
15912 if (mErrorBarSkipSymbol)
15914 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15915 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15916 if (b-x > skipSymbolMargin)
15917 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15919 painter->drawLine(QLineF(a, y, b, y));
15921 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15922 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15924 if (mErrorType == etValue || mErrorType == etBoth)
15926 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15927 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15928 if (valueAxis->rangeReversed())
15931 if (mErrorBarSkipSymbol)
15933 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15934 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15935 if (y-b > skipSymbolMargin)
15936 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15938 painter->drawLine(QLineF(x, a, x, b));
15940 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15941 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15948 called by \ref getPreparedData to determine which data (key) range is visible at the current key
15949 axis range setting, so only that needs to be processed.
15951 \a lower returns an iterator to the lowest data point that needs to be taken into account when
15952 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
15953 lower may still be just outside the visible range.
15955 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
15956 just outside of the visible range.
15958 if the graph contains no data, both \a lower and \a upper point to constEnd.
15960 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15962 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15963 if (mData->isEmpty())
15965 lower = mData->constEnd();
15966 upper = mData->constEnd();
15970 // get visible data range as QMap iterators
15971 QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
15972 QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
15973 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15974 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15976 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15977 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15982 Counts the number of data points between \a lower and \a upper (including them), up to a maximum
15985 This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
15986 used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
15987 only needs to be done until \a maxCount is reached, which should be set to the number of data
15988 points at which adaptive sampling sets in.
15990 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15992 if (upper == mData->constEnd() && lower == mData->constEnd())
15994 QCPDataMap::const_iterator it = lower;
15996 while (it != upper && count < maxCount)
16006 The line data vector generated by e.g. getLinePlotData contains only the line that connects the
16007 data points. If the graph needs to be filled, two additional points need to be added at the
16008 value-zero-line in the lower and upper key positions of the graph. This function calculates these
16009 points and adds them to the end of \a lineData. Since the fill is typically drawn before the line
16010 stroke, these added points need to be removed again after the fill is done, with the
16011 removeFillBasePoints function.
16013 The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
16014 because the data vector generation functions (getLinePlotData etc.) reserve two extra points when
16015 they allocate memory for \a lineData.
16017 \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16019 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16021 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16022 if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
16023 if (lineData->isEmpty()) return;
16025 // append points that close the polygon fill at the key axis:
16026 if (mKeyAxis.data()->orientation() == Qt::Vertical)
16028 *lineData << upperFillBasePoint(lineData->last().y());
16029 *lineData << lowerFillBasePoint(lineData->first().y());
16032 *lineData << upperFillBasePoint(lineData->last().x());
16033 *lineData << lowerFillBasePoint(lineData->first().x());
16039 removes the two points from \a lineData that were added by \ref addFillBasePoints.
16041 \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16043 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16045 if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; }
16046 if (lineData->isEmpty()) return;
16048 lineData->remove(lineData->size()-2, 2);
16053 called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon
16054 on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale
16055 case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative
16056 infinity. So this case is handled separately by just closing the fill polygon on the axis which
16057 lies in the direction towards the zero value.
16059 \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key
16060 axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned
16061 point, respectively.
16063 \see upperFillBasePoint, addFillBasePoints
16065 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16067 QCPAxis *keyAxis = mKeyAxis.data();
16068 QCPAxis *valueAxis = mValueAxis.data();
16069 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16072 if (valueAxis->scaleType() == QCPAxis::stLinear)
16074 if (keyAxis->axisType() == QCPAxis::atLeft)
16076 point.setX(valueAxis->coordToPixel(0));
16077 point.setY(lowerKey);
16078 } else if (keyAxis->axisType() == QCPAxis::atRight)
16080 point.setX(valueAxis->coordToPixel(0));
16081 point.setY(lowerKey);
16082 } else if (keyAxis->axisType() == QCPAxis::atTop)
16084 point.setX(lowerKey);
16085 point.setY(valueAxis->coordToPixel(0));
16086 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16088 point.setX(lowerKey);
16089 point.setY(valueAxis->coordToPixel(0));
16091 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16093 // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16094 // to the axis which is in the direction towards zero
16095 if (keyAxis->orientation() == Qt::Vertical)
16097 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16098 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16099 point.setX(keyAxis->axisRect()->right());
16101 point.setX(keyAxis->axisRect()->left());
16102 point.setY(lowerKey);
16103 } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16105 point.setX(lowerKey);
16106 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16107 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16108 point.setY(keyAxis->axisRect()->top());
16110 point.setY(keyAxis->axisRect()->bottom());
16118 called by \ref addFillBasePoints to conveniently assign the point which closes the fill
16119 polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
16120 scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
16121 negative infinity. So this case is handled separately by just closing the fill polygon on the
16122 axis which lies in the direction towards the zero value.
16124 \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key
16125 axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned
16126 point, respectively.
16128 \see lowerFillBasePoint, addFillBasePoints
16130 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16132 QCPAxis *keyAxis = mKeyAxis.data();
16133 QCPAxis *valueAxis = mValueAxis.data();
16134 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16137 if (valueAxis->scaleType() == QCPAxis::stLinear)
16139 if (keyAxis->axisType() == QCPAxis::atLeft)
16141 point.setX(valueAxis->coordToPixel(0));
16142 point.setY(upperKey);
16143 } else if (keyAxis->axisType() == QCPAxis::atRight)
16145 point.setX(valueAxis->coordToPixel(0));
16146 point.setY(upperKey);
16147 } else if (keyAxis->axisType() == QCPAxis::atTop)
16149 point.setX(upperKey);
16150 point.setY(valueAxis->coordToPixel(0));
16151 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16153 point.setX(upperKey);
16154 point.setY(valueAxis->coordToPixel(0));
16156 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16158 // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16159 // to the axis which is in the direction towards 0
16160 if (keyAxis->orientation() == Qt::Vertical)
16162 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16163 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16164 point.setX(keyAxis->axisRect()->right());
16166 point.setX(keyAxis->axisRect()->left());
16167 point.setY(upperKey);
16168 } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16170 point.setX(upperKey);
16171 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16172 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16173 point.setY(keyAxis->axisRect()->top());
16175 point.setY(keyAxis->axisRect()->bottom());
16183 Generates the polygon needed for drawing channel fills between this graph (data passed via \a
16184 lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
16185 getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
16186 target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
16187 key axes vertical). For increased performance (due to implicit sharing), keep the returned
16190 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16192 if (!mChannelFillGraph)
16193 return QPolygonF();
16195 QCPAxis *keyAxis = mKeyAxis.data();
16196 QCPAxis *valueAxis = mValueAxis.data();
16197 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16198 if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16200 if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16201 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)
16203 if (lineData->isEmpty()) return QPolygonF();
16204 QVector<QPointF> otherData;
16205 mChannelFillGraph.data()->getPlotData(&otherData, 0);
16206 if (otherData.isEmpty()) return QPolygonF();
16207 QVector<QPointF> thisData;
16208 thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16209 for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16210 thisData << lineData->at(i);
16212 // pointers to be able to swap them, depending which data range needs cropping:
16213 QVector<QPointF> *staticData = &thisData;
16214 QVector<QPointF> *croppedData = &otherData;
16216 // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16217 if (keyAxis->orientation() == Qt::Horizontal)
16220 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16221 if (staticData->first().x() > staticData->last().x())
16223 int size = staticData->size();
16224 for (int i=0; i<size/2; ++i)
16225 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16227 if (croppedData->first().x() > croppedData->last().x())
16229 int size = croppedData->size();
16230 for (int i=0; i<size/2; ++i)
16231 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16233 // crop lower bound:
16234 if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16235 qSwap(staticData, croppedData);
16236 int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16237 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16238 croppedData->remove(0, lowBound);
16239 // set lowest point of cropped data to fit exactly key position of first static data
16240 // point via linear interpolation:
16241 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16243 if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16244 slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16247 (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16248 (*croppedData)[0].setX(staticData->first().x());
16250 // crop upper bound:
16251 if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16252 qSwap(staticData, croppedData);
16253 int highBound = findIndexAboveX(croppedData, staticData->last().x());
16254 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16255 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16256 // set highest point of cropped data to fit exactly key position of last static data
16257 // point via linear interpolation:
16258 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16259 int li = croppedData->size()-1; // last index
16260 if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16261 slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16264 (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16265 (*croppedData)[li].setX(staticData->last().x());
16266 } else // mKeyAxis->orientation() == Qt::Vertical
16269 // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16270 // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16271 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16272 if (staticData->first().y() < staticData->last().y())
16274 int size = staticData->size();
16275 for (int i=0; i<size/2; ++i)
16276 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16278 if (croppedData->first().y() < croppedData->last().y())
16280 int size = croppedData->size();
16281 for (int i=0; i<size/2; ++i)
16282 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16284 // crop lower bound:
16285 if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16286 qSwap(staticData, croppedData);
16287 int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16288 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16289 croppedData->remove(0, lowBound);
16290 // set lowest point of cropped data to fit exactly key position of first static data
16291 // point via linear interpolation:
16292 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16294 if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16295 slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16298 (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16299 (*croppedData)[0].setY(staticData->first().y());
16301 // crop upper bound:
16302 if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16303 qSwap(staticData, croppedData);
16304 int highBound = findIndexBelowY(croppedData, staticData->last().y());
16305 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16306 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16307 // set highest point of cropped data to fit exactly key position of last static data
16308 // point via linear interpolation:
16309 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16310 int li = croppedData->size()-1; // last index
16311 if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16312 slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16315 (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16316 (*croppedData)[li].setY(staticData->last().y());
16320 for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16321 thisData << otherData.at(i);
16322 return QPolygonF(thisData);
16327 Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in
16328 \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16330 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16332 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16334 for (int i=data->size()-1; i>=0; --i)
16336 if (data->at(i).x() < x)
16338 if (i<data->size()-1)
16341 return data->size()-1;
16349 Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in
16350 \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16352 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16354 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16356 for (int i=0; i<data->size(); ++i)
16358 if (data->at(i).x() > x)
16371 Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in
16372 \a data points are ordered descending, as is the case when plotting with vertical key axis.
16374 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16376 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16378 for (int i=0; i<data->size(); ++i)
16380 if (data->at(i).y() < y)
16393 Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
16394 pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
16397 If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape
16398 is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0.
16400 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16402 if (mData->isEmpty())
16404 if (mLineStyle == lsNone && mScatterStyle.isNone())
16407 // calculate minimum distances to graph representation:
16408 if (mLineStyle == lsNone)
16410 // no line displayed, only calculate distance to scatter points:
16411 QVector<QCPData> scatterData;
16412 getScatterPlotData(&scatterData);
16413 if (scatterData.size() > 0)
16415 double minDistSqr = std::numeric_limits<double>::max();
16416 for (int i=0; i<scatterData.size(); ++i)
16418 double currentDistSqr = QVector2D(coordsToPixels(scatterData.at(i).key, scatterData.at(i).value)-pixelPoint).lengthSquared();
16419 if (currentDistSqr < minDistSqr)
16420 minDistSqr = currentDistSqr;
16422 return qSqrt(minDistSqr);
16423 } else // no data available in view to calculate distance to
16427 // line displayed, calculate distance to line segments:
16428 QVector<QPointF> lineData;
16429 getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16430 if (lineData.size() > 1) // at least one line segment, compare distance to line segments
16432 double minDistSqr = std::numeric_limits<double>::max();
16433 if (mLineStyle == lsImpulse)
16435 // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16436 for (int i=0; i<lineData.size()-1; i+=2) // iterate pairs
16438 double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16439 if (currentDistSqr < minDistSqr)
16440 minDistSqr = currentDistSqr;
16444 // all other line plots (line and step) connect points directly:
16445 for (int i=0; i<lineData.size()-1; ++i)
16447 double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16448 if (currentDistSqr < minDistSqr)
16449 minDistSqr = currentDistSqr;
16452 return qSqrt(minDistSqr);
16453 } else if (lineData.size() > 0) // only single data point, calculate distance to that point
16455 return QVector2D(lineData.at(0)-pixelPoint).length();
16456 } else // no data available in view to calculate distance to
16463 Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in
16464 \a data points are ordered descending, as is the case when plotting with vertical key axis (since
16465 keys are ordered ascending).
16467 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16469 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16471 for (int i=data->size()-1; i>=0; --i)
16473 if (data->at(i).y() > y)
16475 if (i<data->size()-1)
16478 return data->size()-1;
16484 /* inherits documentation from base class */
16485 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16487 // just call the specialized version which takes an additional argument whether error bars
16488 // should also be taken into consideration for range calculation. We set this to true here.
16489 return getKeyRange(foundRange, inSignDomain, true);
16492 /* inherits documentation from base class */
16493 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16495 // just call the specialized version which takes an additional argument whether error bars
16496 // should also be taken into consideration for range calculation. We set this to true here.
16497 return getValueRange(foundRange, inSignDomain, true);
16502 Allows to specify whether the error bars should be included in the range calculation.
16504 \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
16506 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16509 bool haveLower = false;
16510 bool haveUpper = false;
16512 double current, currentErrorMinus, currentErrorPlus;
16514 if (inSignDomain == sdBoth) // range may be anywhere
16516 QCPDataMap::const_iterator it = mData->constBegin();
16517 while (it != mData->constEnd())
16519 if (!qIsNaN(it.value().value))
16521 current = it.value().key;
16522 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16523 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16524 if (current-currentErrorMinus < range.lower || !haveLower)
16526 range.lower = current-currentErrorMinus;
16529 if (current+currentErrorPlus > range.upper || !haveUpper)
16531 range.upper = current+currentErrorPlus;
16537 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16539 QCPDataMap::const_iterator it = mData->constBegin();
16540 while (it != mData->constEnd())
16542 if (!qIsNaN(it.value().value))
16544 current = it.value().key;
16545 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16546 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16547 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16549 range.lower = current-currentErrorMinus;
16552 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16554 range.upper = current+currentErrorPlus;
16557 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16559 if ((current < range.lower || !haveLower) && current < 0)
16561 range.lower = current;
16564 if ((current > range.upper || !haveUpper) && current < 0)
16566 range.upper = current;
16573 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16575 QCPDataMap::const_iterator it = mData->constBegin();
16576 while (it != mData->constEnd())
16578 if (!qIsNaN(it.value().value))
16580 current = it.value().key;
16581 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16582 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16583 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16585 range.lower = current-currentErrorMinus;
16588 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16590 range.upper = current+currentErrorPlus;
16593 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16595 if ((current < range.lower || !haveLower) && current > 0)
16597 range.lower = current;
16600 if ((current > range.upper || !haveUpper) && current > 0)
16602 range.upper = current;
16611 foundRange = haveLower && haveUpper;
16617 Allows to specify whether the error bars should be included in the range calculation.
16619 \see getValueRange(bool &foundRange, SignDomain inSignDomain)
16621 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16624 bool haveLower = false;
16625 bool haveUpper = false;
16627 double current, currentErrorMinus, currentErrorPlus;
16629 if (inSignDomain == sdBoth) // range may be anywhere
16631 QCPDataMap::const_iterator it = mData->constBegin();
16632 while (it != mData->constEnd())
16634 current = it.value().value;
16635 if (!qIsNaN(current))
16637 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16638 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16639 if (current-currentErrorMinus < range.lower || !haveLower)
16641 range.lower = current-currentErrorMinus;
16644 if (current+currentErrorPlus > range.upper || !haveUpper)
16646 range.upper = current+currentErrorPlus;
16652 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16654 QCPDataMap::const_iterator it = mData->constBegin();
16655 while (it != mData->constEnd())
16657 current = it.value().value;
16658 if (!qIsNaN(current))
16660 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16661 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16662 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16664 range.lower = current-currentErrorMinus;
16667 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16669 range.upper = current+currentErrorPlus;
16672 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16674 if ((current < range.lower || !haveLower) && current < 0)
16676 range.lower = current;
16679 if ((current > range.upper || !haveUpper) && current < 0)
16681 range.upper = current;
16688 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16690 QCPDataMap::const_iterator it = mData->constBegin();
16691 while (it != mData->constEnd())
16693 current = it.value().value;
16694 if (!qIsNaN(current))
16696 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16697 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16698 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16700 range.lower = current-currentErrorMinus;
16703 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16705 range.upper = current+currentErrorPlus;
16708 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16710 if ((current < range.lower || !haveLower) && current > 0)
16712 range.lower = current;
16715 if ((current > range.upper || !haveUpper) && current > 0)
16717 range.upper = current;
16726 foundRange = haveLower && haveUpper;
16731 ////////////////////////////////////////////////////////////////////////////////////////////////////
16732 //////////////////// QCPCurveData
16733 ////////////////////////////////////////////////////////////////////////////////////////////////////
16735 /*! \class QCPCurveData
16736 \brief Holds the data of one single data point for QCPCurve.
16738 The container for storing multiple data points is \ref QCPCurveDataMap.
16740 The stored data is:
16741 \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
16742 \li \a key: coordinate on the key axis of this curve point
16743 \li \a value: coordinate on the value axis of this curve point
16745 \see QCPCurveDataMap
16749 Constructs a curve data point with t, key and value set to zero.
16751 QCPCurveData::QCPCurveData() :
16759 Constructs a curve data point with the specified \a t, \a key and \a value.
16761 QCPCurveData::QCPCurveData(double t, double key, double value) :
16769 ////////////////////////////////////////////////////////////////////////////////////////////////////
16770 //////////////////// QCPCurve
16771 ////////////////////////////////////////////////////////////////////////////////////////////////////
16773 /*! \class QCPCurve
16774 \brief A plottable representing a parametric curve in a plot.
16776 \image html QCPCurve.png
16778 Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate,
16779 so their visual representation can have \a loops. This is realized by introducing a third
16780 coordinate \a t, which defines the order of the points described by the other two coordinates \a
16783 To plot data, assign it with the \ref setData or \ref addData functions.
16785 Gaps in the curve can be created by adding data points with NaN as key and value
16786 (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
16789 \section appearance Changing the appearance
16791 The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
16792 \section usage Usage
16794 Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
16795 the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
16797 Usually, you first create an instance and add it to the customPlot:
16798 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1
16799 and then modify the properties of the newly created plottable, e.g.:
16800 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2
16804 Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
16805 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
16806 the same orientation. If either of these restrictions is violated, a corresponding message is
16807 printed to the debug output (qDebug), the construction is not aborted, though.
16809 The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
16810 then takes ownership of the graph.
16812 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16813 QCPAbstractPlottable(keyAxis, valueAxis)
16815 mData = new QCPCurveDataMap;
16816 mPen.setColor(Qt::blue);
16817 mPen.setStyle(Qt::SolidLine);
16818 mBrush.setColor(Qt::blue);
16819 mBrush.setStyle(Qt::NoBrush);
16820 mSelectedPen = mPen;
16821 mSelectedPen.setWidthF(2.5);
16822 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16823 mSelectedBrush = mBrush;
16825 setScatterStyle(QCPScatterStyle());
16826 setLineStyle(lsLine);
16829 QCPCurve::~QCPCurve()
16835 Replaces the current data with the provided \a data.
16837 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
16838 takes ownership of the passed data and replaces the internal data pointer with it. This is
16839 significantly faster than copying for large datasets.
16841 void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
16845 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
16860 Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
16861 provided vectors should have equal length. Else, the number of added points will be the size of
16862 the smallest vector.
16864 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16868 n = qMin(n, key.size());
16869 n = qMin(n, value.size());
16870 QCPCurveData newData;
16871 for (int i=0; i<n; ++i)
16874 newData.key = key[i];
16875 newData.value = value[i];
16876 mData->insertMulti(newData.t, newData);
16882 Replaces the current data with the provided \a key and \a value pairs. The t parameter
16883 of each data point will be set to the integer index of the respective key/value pair.
16885 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16888 int n = key.size();
16889 n = qMin(n, value.size());
16890 QCPCurveData newData;
16891 for (int i=0; i<n; ++i)
16893 newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16894 newData.key = key[i];
16895 newData.value = value[i];
16896 mData->insertMulti(newData.t, newData);
16901 Sets the visual appearance of single data points in the plot. If set to \ref
16902 QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate
16905 \see QCPScatterStyle, setLineStyle
16907 void QCPCurve::setScatterStyle(const QCPScatterStyle &style)
16909 mScatterStyle = style;
16913 Sets how the single data points are connected in the plot or how they are represented visually
16914 apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
16915 setScatterStyle to the desired scatter style.
16917 \see setScatterStyle
16919 void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
16921 mLineStyle = style;
16925 Adds the provided data points in \a dataMap to the current data.
16928 void QCPCurve::addData(const QCPCurveDataMap &dataMap)
16930 mData->unite(dataMap);
16934 Adds the provided single data point in \a data to the current data.
16937 void QCPCurve::addData(const QCPCurveData &data)
16939 mData->insertMulti(data.t, data);
16943 Adds the provided single data point as \a t, \a key and \a value tuple to the current data
16946 void QCPCurve::addData(double t, double key, double value)
16948 QCPCurveData newData;
16951 newData.value = value;
16952 mData->insertMulti(newData.t, newData);
16957 Adds the provided single data point as \a key and \a value pair to the current data The t
16958 parameter of the data point is set to the t of the last data point plus 1. If there is no last
16959 data point, t will be set to 0.
16963 void QCPCurve::addData(double key, double value)
16965 QCPCurveData newData;
16966 if (!mData->isEmpty())
16967 newData.t = (mData->constEnd()-1).key()+1;
16971 newData.value = value;
16972 mData->insertMulti(newData.t, newData);
16976 Adds the provided data points as \a t, \a key and \a value tuples to the current data.
16979 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16982 n = qMin(n, keys.size());
16983 n = qMin(n, values.size());
16984 QCPCurveData newData;
16985 for (int i=0; i<n; ++i)
16988 newData.key = keys[i];
16989 newData.value = values[i];
16990 mData->insertMulti(newData.t, newData);
16995 Removes all data points with curve parameter t smaller than \a t.
16996 \see addData, clearData
16998 void QCPCurve::removeDataBefore(double t)
17000 QCPCurveDataMap::iterator it = mData->begin();
17001 while (it != mData->end() && it.key() < t)
17002 it = mData->erase(it);
17006 Removes all data points with curve parameter t greater than \a t.
17007 \see addData, clearData
17009 void QCPCurve::removeDataAfter(double t)
17011 if (mData->isEmpty()) return;
17012 QCPCurveDataMap::iterator it = mData->upperBound(t);
17013 while (it != mData->end())
17014 it = mData->erase(it);
17018 Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
17019 greater or equal to \a tot, the function does nothing. To remove a single data point with known
17020 t, use \ref removeData(double t).
17022 \see addData, clearData
17024 void QCPCurve::removeData(double fromt, double tot)
17026 if (fromt >= tot || mData->isEmpty()) return;
17027 QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17028 QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17029 while (it != itEnd)
17030 it = mData->erase(it);
17035 Removes a single data point at curve parameter \a t. If the position is not known with absolute
17036 precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
17037 interval around the suspected position, depeding on the precision with which the curve parameter
17040 \see addData, clearData
17042 void QCPCurve::removeData(double t)
17048 Removes all data points.
17049 \see removeData, removeDataAfter, removeDataBefore
17051 void QCPCurve::clearData()
17056 /* inherits documentation from base class */
17057 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17060 if ((onlySelectable && !mSelectable) || mData->isEmpty())
17062 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17064 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17065 return pointDistance(pos);
17070 /* inherits documentation from base class */
17071 void QCPCurve::draw(QCPPainter *painter)
17073 if (mData->isEmpty()) return;
17075 // allocate line vector:
17076 QVector<QPointF> *lineData = new QVector<QPointF>;
17078 // fill with curve data:
17079 getCurveData(lineData);
17081 // check data validity if flag set:
17082 #ifdef QCUSTOMPLOT_CHECK_DATA
17083 QCPCurveDataMap::const_iterator it;
17084 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17086 if (QCP::isInvalidData(it.value().t) ||
17087 QCP::isInvalidData(it.value().key, it.value().value))
17088 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17092 // draw curve fill:
17093 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17095 applyFillAntialiasingHint(painter);
17096 painter->setPen(Qt::NoPen);
17097 painter->setBrush(mainBrush());
17098 painter->drawPolygon(QPolygonF(*lineData));
17101 // draw curve line:
17102 if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17104 applyDefaultAntialiasingHint(painter);
17105 painter->setPen(mainPen());
17106 painter->setBrush(Qt::NoBrush);
17107 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17108 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17109 painter->pen().style() == Qt::SolidLine &&
17110 !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17111 !painter->modes().testFlag(QCPPainter::pmNoCaching))
17114 bool lastIsNan = false;
17115 const int lineDataSize = lineData->size();
17116 while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
17118 ++i; // because drawing works in 1 point retrospect
17119 while (i < lineDataSize)
17121 if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17124 painter->drawLine(lineData->at(i-1), lineData->at(i));
17133 int segmentStart = 0;
17135 const int lineDataSize = lineData->size();
17136 while (i < lineDataSize)
17138 if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17140 painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17141 segmentStart = i+1;
17145 // draw last segment:
17146 painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
17151 if (!mScatterStyle.isNone())
17152 drawScatterPlot(painter, lineData);
17154 // free allocated line data:
17158 /* inherits documentation from base class */
17159 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17162 if (mBrush.style() != Qt::NoBrush)
17164 applyFillAntialiasingHint(painter);
17165 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17167 // draw line vertically centered:
17168 if (mLineStyle != lsNone)
17170 applyDefaultAntialiasingHint(painter);
17171 painter->setPen(mPen);
17172 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
17174 // draw scatter symbol:
17175 if (!mScatterStyle.isNone())
17177 applyScattersAntialiasingHint(painter);
17178 // scale scatter pixmap if it's too large to fit in legend icon rect:
17179 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17181 QCPScatterStyle scaledStyle(mScatterStyle);
17182 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17183 scaledStyle.applyTo(painter, mPen);
17184 scaledStyle.drawShape(painter, QRectF(rect).center());
17187 mScatterStyle.applyTo(painter, mPen);
17188 mScatterStyle.drawShape(painter, QRectF(rect).center());
17195 Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
17196 the line style and are always drawn if scatter shape is not \ref QCPScatterStyle::ssNone.
17198 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17200 // draw scatter point symbols:
17201 applyScattersAntialiasingHint(painter);
17202 mScatterStyle.applyTo(painter, mPen);
17203 for (int i=0; i<pointData->size(); ++i)
17204 if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17205 mScatterStyle.drawShape(painter, pointData->at(i));
17210 called by QCPCurve::draw to generate a point vector (in pixel coordinates) which represents the
17213 Line segments that aren't visible in the current axis rect are handled in an optimized way. They
17214 are projected onto a rectangle slightly larger than the visible axis rect and simplified
17215 regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside
17216 the visible axis rect by generating new temporary points on the outer rect if necessary.
17218 Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref
17219 getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints.
17221 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17223 QCPAxis *keyAxis = mKeyAxis.data();
17224 QCPAxis *valueAxis = mValueAxis.data();
17225 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17227 // add margins to rect to compensate for stroke width
17228 double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17229 if (!mScatterStyle.isNone())
17230 strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17231 double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17232 double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17233 double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17234 double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17236 QCPCurveDataMap::const_iterator it = mData->constBegin();
17237 QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
17238 int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17239 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)
17240 while (it != mData->constEnd())
17242 currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17243 if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17245 if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17247 QPointF crossA, crossB;
17248 if (prevRegion == 5) // we're coming from R, so add this point optimized
17250 lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17251 // 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
17252 *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17253 } else if (mayTraverse(prevRegion, currentRegion) &&
17254 getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17256 // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17257 QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17258 getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17259 if (it != mData->constBegin())
17261 *lineData << beforeTraverseCornerPoints;
17262 lineData->append(crossA);
17263 lineData->append(crossB);
17264 *lineData << afterTraverseCornerPoints;
17267 lineData->append(crossB);
17268 *lineData << afterTraverseCornerPoints;
17269 trailingPoints << beforeTraverseCornerPoints << crossA ;
17271 } 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)
17273 *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17275 } else // segment does end in R, so we add previous point optimized and this point at original position
17277 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
17278 trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17280 lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17281 lineData->append(coordsToPixels(it.value().key, it.value().value));
17283 } else // region didn't change
17285 if (currentRegion == 5) // still in R, keep adding original points
17287 lineData->append(coordsToPixels(it.value().key, it.value().value));
17288 } else // still outside R, no need to add anything
17290 // see how this is not doing anything? That's the main optimization...
17294 prevRegion = currentRegion;
17297 *lineData << trailingPoints;
17302 This function is part of the curve optimization algorithm of \ref getCurveData.
17304 It returns the region of the given point (\a x, \a y) with respect to a rectangle defined by \a
17305 rectLeft, \a rectTop, \a rectRight, and \a rectBottom.
17307 The regions are enumerated from top to bottom and left to right:
17309 <table style="width:10em; text-align:center">
17310 <tr><td>1</td><td>4</td><td>7</td></tr>
17311 <tr><td>2</td><td style="border:1px solid black">5</td><td>8</td></tr>
17312 <tr><td>3</td><td>6</td><td>9</td></tr>
17315 With the rectangle being region 5, and the outer regions extending infinitely outwards. In the
17316 curve optimization algorithm, region 5 is considered to be the visible portion of the plot.
17318 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17320 if (x < rectLeft) // region 123
17324 else if (y < rectBottom)
17328 } else if (x > rectRight) // region 789
17332 else if (y < rectBottom)
17336 } else // region 456
17340 else if (y < rectBottom)
17349 This function is part of the curve optimization algorithm of \ref getCurveData.
17351 This method is used in case the current segment passes from inside the visible rect (region 5,
17352 see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by
17353 the line connecting (\a key, \a value) with (\a otherKey, \a otherValue).
17355 It returns the intersection point of the segment with the border of region 5.
17357 For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or
17358 whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or
17359 leaving it. It is important though that \a otherRegion correctly identifies the other region not
17362 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17364 double intersectKey = rectLeft; // initial value is just fail-safe
17365 double intersectValue = rectTop; // initial value is just fail-safe
17366 switch (otherRegion)
17368 case 1: // top and left edge
17370 intersectValue = rectTop;
17371 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17372 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17374 intersectKey = rectLeft;
17375 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17379 case 2: // left edge
17381 intersectKey = rectLeft;
17382 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17385 case 3: // bottom and left edge
17387 intersectValue = rectBottom;
17388 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17389 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17391 intersectKey = rectLeft;
17392 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17396 case 4: // top edge
17398 intersectValue = rectTop;
17399 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17404 break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17406 case 6: // bottom edge
17408 intersectValue = rectBottom;
17409 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17412 case 7: // top and right edge
17414 intersectValue = rectTop;
17415 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17416 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17418 intersectKey = rectRight;
17419 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17423 case 8: // right edge
17425 intersectKey = rectRight;
17426 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17429 case 9: // bottom and right edge
17431 intersectValue = rectBottom;
17432 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17433 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17435 intersectKey = rectRight;
17436 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17441 return coordsToPixels(intersectKey, intersectValue);
17446 This function is part of the curve optimization algorithm of \ref getCurveData.
17448 In situations where a single segment skips over multiple regions it might become necessary to add
17449 extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment
17450 doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts.
17451 This method provides these points that must be added, assuming the original segment doesn't
17452 start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by
17453 \ref getTraverseCornerPoints.)
17455 For example, consider a segment which directly goes from region 4 to 2 but originally is far out
17456 to the top left such that it doesn't cross region 5. Naively optimizing these points by
17457 projecting them on the top and left borders of region 5 will create a segment that surely crosses
17458 5, creating a visual artifact in the plot. This method prevents this by providing extra points at
17459 the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without
17462 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
17464 QVector<QPointF> result;
17465 switch (prevRegion)
17469 switch (currentRegion)
17471 case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17472 case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17473 case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17474 case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17475 case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17476 case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17477 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
17478 if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17479 { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17481 { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17489 switch (currentRegion)
17491 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17492 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17493 case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17494 case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17495 case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17496 case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17502 switch (currentRegion)
17504 case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17505 case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17506 case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17507 case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17508 case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17509 case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17510 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
17511 if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17512 { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17514 { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17522 switch (currentRegion)
17524 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17525 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17526 case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17527 case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17528 case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17529 case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17535 switch (currentRegion)
17537 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17538 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17539 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17540 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17546 switch (currentRegion)
17548 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17549 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17550 case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17551 case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17552 case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17553 case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17559 switch (currentRegion)
17561 case 4: { result << coordsToPixels(rectRight, rectTop); break; }
17562 case 8: { result << coordsToPixels(rectRight, rectTop); break; }
17563 case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
17564 case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
17565 case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17566 case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17567 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
17568 if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17569 { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17571 { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17579 switch (currentRegion)
17581 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17582 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17583 case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17584 case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17585 case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17586 case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17592 switch (currentRegion)
17594 case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
17595 case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
17596 case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
17597 case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
17598 case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17599 case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17600 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
17601 if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17602 { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17604 { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17616 This function is part of the curve optimization algorithm of \ref getCurveData.
17618 This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref
17619 getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion
17620 nor \a currentRegion is 5 itself.
17622 If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the
17623 segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref
17626 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
17628 switch (prevRegion)
17632 switch (currentRegion)
17637 case 3: return false;
17638 default: return true;
17643 switch (currentRegion)
17646 case 3: return false;
17647 default: return true;
17652 switch (currentRegion)
17657 case 9: return false;
17658 default: return true;
17663 switch (currentRegion)
17666 case 7: return false;
17667 default: return true;
17670 case 5: return false; // should never occur
17673 switch (currentRegion)
17676 case 9: return false;
17677 default: return true;
17682 switch (currentRegion)
17687 case 9: return false;
17688 default: return true;
17693 switch (currentRegion)
17696 case 9: return false;
17697 default: return true;
17702 switch (currentRegion)
17707 case 7: return false;
17708 default: return true;
17711 default: return true;
17718 This function is part of the curve optimization algorithm of \ref getCurveData.
17720 This method assumes that the \ref mayTraverse test has returned true, so there is a chance the
17721 segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible
17724 The return value of this method indicates whether the segment actually traverses region 5 or not.
17726 If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and
17727 exit points of region 5. They will become the optimized points for that segment.
17729 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
17731 QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
17732 if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
17734 // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17735 intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
17736 intersections.append(QPointF(key, rectTop));
17737 } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
17739 // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17740 intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
17741 intersections.append(QPointF(rectRight, value));
17742 } else // line is skewed
17745 double keyPerValue = (key-prevKey)/(value-prevValue);
17746 // check top of rect:
17747 gamma = prevKey + (rectTop-prevValue)*keyPerValue;
17748 if (gamma >= rectLeft && gamma <= rectRight)
17749 intersections.append(QPointF(gamma, rectTop));
17750 // check bottom of rect:
17751 gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
17752 if (gamma >= rectLeft && gamma <= rectRight)
17753 intersections.append(QPointF(gamma, rectBottom));
17754 double valuePerKey = 1.0/keyPerValue;
17755 // check left of rect:
17756 gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
17757 if (gamma >= rectBottom && gamma <= rectTop)
17758 intersections.append(QPointF(rectLeft, gamma));
17759 // check right of rect:
17760 gamma = prevValue + (rectRight-prevKey)*valuePerKey;
17761 if (gamma >= rectBottom && gamma <= rectTop)
17762 intersections.append(QPointF(rectRight, gamma));
17765 // handle cases where found points isn't exactly 2:
17766 if (intersections.size() > 2)
17768 // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
17769 double distSqrMax = 0;
17771 for (int i=0; i<intersections.size()-1; ++i)
17773 for (int k=i+1; k<intersections.size(); ++k)
17775 QPointF distPoint = intersections.at(i)-intersections.at(k);
17776 double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
17777 if (distSqr > distSqrMax)
17779 pv1 = intersections.at(i);
17780 pv2 = intersections.at(k);
17781 distSqrMax = distSqr;
17785 intersections = QList<QPointF>() << pv1 << pv2;
17786 } else if (intersections.size() != 2)
17788 // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
17792 // possibly re-sort points so optimized point segment has same direction as original segment:
17793 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
17794 intersections.move(0, 1);
17795 crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
17796 crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
17802 This function is part of the curve optimization algorithm of \ref getCurveData.
17804 This method assumes that the \ref getTraverse test has returned true, so the segment definitely
17805 traverses the visible region 5 when going from \a prevRegion to \a currentRegion.
17807 In certain situations it is not sufficient to merely generate the entry and exit points of the
17808 segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in
17809 addition to traversing region 5, skips another region outside of region 5, which makes it
17810 necessary to add an optimized corner point there (very similar to the job \ref
17811 getOptimizedCornerPoints does for segments that are completely in outside regions and don't
17814 As an example, consider a segment going from region 1 to region 6, traversing the lower left
17815 corner of region 5. In this configuration, the segment additionally crosses the border between
17816 region 1 and 2 before entering region 5. This makes it necessary to add an additional point in
17817 the top left corner, before adding the optimized traverse points. So in this case, the output
17818 parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be
17821 In some cases, such as when going from region 1 to 9, it may even be necessary to add additional
17822 corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse
17823 return the respective corner points.
17825 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
17827 switch (prevRegion)
17831 switch (currentRegion)
17833 case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17834 case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17835 case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17841 switch (currentRegion)
17843 case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17844 case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17850 switch (currentRegion)
17852 case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17853 case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17854 case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17860 switch (currentRegion)
17862 case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17863 case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17867 case 5: { break; } // shouldn't happen because this method only handles full traverses
17870 switch (currentRegion)
17872 case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17873 case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17879 switch (currentRegion)
17881 case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17882 case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17883 case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17889 switch (currentRegion)
17891 case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17892 case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17898 switch (currentRegion)
17900 case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17901 case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17902 case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17911 Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
17912 pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
17915 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17917 if (mData->isEmpty())
17919 qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17922 if (mData->size() == 1)
17924 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17925 return QVector2D(dataPoint-pixelPoint).length();
17928 // calculate minimum distance to line segments:
17929 QVector<QPointF> *lineData = new QVector<QPointF>;
17930 getCurveData(lineData);
17931 double minDistSqr = std::numeric_limits<double>::max();
17932 for (int i=0; i<lineData->size()-1; ++i)
17934 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17935 if (currentDistSqr < minDistSqr)
17936 minDistSqr = currentDistSqr;
17939 return qSqrt(minDistSqr);
17942 /* inherits documentation from base class */
17943 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17946 bool haveLower = false;
17947 bool haveUpper = false;
17951 QCPCurveDataMap::const_iterator it = mData->constBegin();
17952 while (it != mData->constEnd())
17954 current = it.value().key;
17955 if (!qIsNaN(current) && !qIsNaN(it.value().value))
17957 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17959 if (current < range.lower || !haveLower)
17961 range.lower = current;
17964 if (current > range.upper || !haveUpper)
17966 range.upper = current;
17974 foundRange = haveLower && haveUpper;
17978 /* inherits documentation from base class */
17979 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17982 bool haveLower = false;
17983 bool haveUpper = false;
17987 QCPCurveDataMap::const_iterator it = mData->constBegin();
17988 while (it != mData->constEnd())
17990 current = it.value().value;
17991 if (!qIsNaN(current) && !qIsNaN(it.value().key))
17993 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17995 if (current < range.lower || !haveLower)
17997 range.lower = current;
18000 if (current > range.upper || !haveUpper)
18002 range.upper = current;
18010 foundRange = haveLower && haveUpper;
18015 ////////////////////////////////////////////////////////////////////////////////////////////////////
18016 //////////////////// QCPBarsGroup
18017 ////////////////////////////////////////////////////////////////////////////////////////////////////
18019 /*! \class QCPBarsGroup
18020 \brief Groups multiple QCPBars together so they appear side by side
18022 \image html QCPBarsGroup.png
18024 When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable
18025 to have them appearing next to each other at each key. This is what adding the respective QCPBars
18026 plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each
18027 other, see \ref QCPBars::moveAbove.)
18029 \section qcpbarsgroup-usage Usage
18031 To add a QCPBars plottable to the group, create a new group and then add the respective bars
18033 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation
18034 Alternatively to appending to the group like shown above, you can also set the group on the
18035 QCPBars plottable via \ref QCPBars::setBarsGroup.
18037 The spacing between the bars can be configured via \ref setSpacingType and \ref setSpacing. The
18038 bars in this group appear in the plot in the order they were appended. To insert a bars plottable
18039 at a certain index position, or to reposition a bars plottable which is already in the group, use
18042 To remove specific bars from the group, use either \ref remove or call \ref
18043 QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable.
18045 To clear the entire group, call \ref clear, or simply delete the group.
18047 \section qcpbarsgroup-example Example
18049 The image above is generated with the following code:
18050 \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example
18053 /* start of documentation of inline functions */
18055 /*! \fn QList<QCPBars*> QCPBarsGroup::bars() const
18057 Returns all bars currently in this group.
18059 \see bars(int index)
18062 /*! \fn int QCPBarsGroup::size() const
18064 Returns the number of QCPBars plottables that are part of this group.
18068 /*! \fn bool QCPBarsGroup::isEmpty() const
18070 Returns whether this bars group is empty.
18075 /*! \fn bool QCPBarsGroup::contains(QCPBars *bars)
18077 Returns whether the specified \a bars plottable is part of this group.
18081 /* end of documentation of inline functions */
18084 Constructs a new bars group for the specified QCustomPlot instance.
18086 QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :
18087 QObject(parentPlot),
18088 mParentPlot(parentPlot),
18089 mSpacingType(stAbsolute),
18094 QCPBarsGroup::~QCPBarsGroup()
18100 Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType.
18102 The actual spacing can then be specified with \ref setSpacing.
18106 void QCPBarsGroup::setSpacingType(SpacingType spacingType)
18108 mSpacingType = spacingType;
18112 Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is
18113 defined by the current \ref SpacingType, which can be set with \ref setSpacingType.
18115 \see setSpacingType
18117 void QCPBarsGroup::setSpacing(double spacing)
18119 mSpacing = spacing;
18123 Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars
18128 QCPBars *QCPBarsGroup::bars(int index) const
18130 if (index >= 0 && index < mBars.size())
18132 return mBars.at(index);
18135 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18141 Removes all QCPBars plottables from this group.
18145 void QCPBarsGroup::clear()
18147 foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
18148 bars->setBarsGroup(0); // removes itself via removeBars
18152 Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref
18153 QCPBars::setBarsGroup on the \a bars instance.
18155 \see insert, remove
18157 void QCPBarsGroup::append(QCPBars *bars)
18161 qDebug() << Q_FUNC_INFO << "bars is 0";
18165 if (!mBars.contains(bars))
18166 bars->setBarsGroup(this);
18168 qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18172 Inserts the specified \a bars plottable into this group at the specified index position \a i.
18173 This gives you full control over the ordering of the bars.
18175 \a bars may already be part of this group. In that case, \a bars is just moved to the new index
18178 \see append, remove
18180 void QCPBarsGroup::insert(int i, QCPBars *bars)
18184 qDebug() << Q_FUNC_INFO << "bars is 0";
18188 // first append to bars list normally:
18189 if (!mBars.contains(bars))
18190 bars->setBarsGroup(this);
18191 // then move to according position:
18192 mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18196 Removes the specified \a bars plottable from this group.
18198 \see contains, clear
18200 void QCPBarsGroup::remove(QCPBars *bars)
18204 qDebug() << Q_FUNC_INFO << "bars is 0";
18208 if (mBars.contains(bars))
18209 bars->setBarsGroup(0);
18211 qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18216 Adds the specified \a bars to the internal mBars list of bars. This method does not change the
18217 barsGroup property on \a bars.
18219 \see unregisterBars
18221 void QCPBarsGroup::registerBars(QCPBars *bars)
18223 if (!mBars.contains(bars))
18224 mBars.append(bars);
18229 Removes the specified \a bars from the internal mBars list of bars. This method does not change
18230 the barsGroup property on \a bars.
18234 void QCPBarsGroup::unregisterBars(QCPBars *bars)
18236 mBars.removeOne(bars);
18241 Returns the pixel offset in the key dimension the specified \a bars plottable should have at the
18242 given key coordinate \a keyCoord. The offset is relative to the pixel position of the key
18243 coordinate \a keyCoord.
18245 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18247 // find list of all base bars in case some mBars are stacked:
18248 QList<const QCPBars*> baseBars;
18249 foreach (const QCPBars *b, mBars)
18251 while (b->barBelow())
18253 if (!baseBars.contains(b))
18254 baseBars.append(b);
18256 // find base bar this "bars" is stacked on:
18257 const QCPBars *thisBase = bars;
18258 while (thisBase->barBelow())
18259 thisBase = thisBase->barBelow();
18261 // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18263 int index = baseBars.indexOf(thisBase);
18267 double lowerPixelWidth, upperPixelWidth;
18268 if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18271 } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18273 if (baseBars.size() % 2 == 0) // even number of bars
18275 startIndex = baseBars.size()/2-1;
18276 result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18277 } else // uneven number of bars
18279 startIndex = (baseBars.size()-1)/2-1;
18280 baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18281 result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18282 result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18284 for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18286 baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18287 result -= qAbs(upperPixelWidth-lowerPixelWidth);
18288 result -= getPixelSpacing(baseBars.at(i), keyCoord);
18290 // finally half of our bars width:
18291 baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18292 result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18293 } else // bar is to the right of center
18295 if (baseBars.size() % 2 == 0) // even number of bars
18297 startIndex = baseBars.size()/2;
18298 result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18299 } else // uneven number of bars
18301 startIndex = (baseBars.size()-1)/2+1;
18302 baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18303 result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18304 result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18306 for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18308 baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18309 result += qAbs(upperPixelWidth-lowerPixelWidth);
18310 result += getPixelSpacing(baseBars.at(i), keyCoord);
18312 // finally half of our bars width:
18313 baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18314 result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18322 Returns the spacing in pixels which is between this \a bars and the following one, both at the
18323 key coordinate \a keyCoord.
18325 \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only
18326 needed to get acces to the key axis transformation and axis rect for the modes \ref
18327 stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in
18328 \ref stPlotCoords on a logarithmic axis.
18330 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18332 switch (mSpacingType)
18338 case stAxisRectRatio:
18340 if (bars->keyAxis()->orientation() == Qt::Horizontal)
18341 return bars->keyAxis()->axisRect()->width()*mSpacing;
18343 return bars->keyAxis()->axisRect()->height()*mSpacing;
18347 double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18348 return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18355 ////////////////////////////////////////////////////////////////////////////////////////////////////
18356 //////////////////// QCPBarData
18357 ////////////////////////////////////////////////////////////////////////////////////////////////////
18359 /*! \class QCPBarData
18360 \brief Holds the data of one single data point (one bar) for QCPBars.
18362 The container for storing multiple data points is \ref QCPBarDataMap.
18364 The stored data is:
18365 \li \a key: coordinate on the key axis of this bar
18366 \li \a value: height coordinate on the value axis of this bar
18368 \see QCPBarDataaMap
18372 Constructs a bar data point with key and value set to zero.
18374 QCPBarData::QCPBarData() :
18381 Constructs a bar data point with the specified \a key and \a value.
18383 QCPBarData::QCPBarData(double key, double value) :
18390 ////////////////////////////////////////////////////////////////////////////////////////////////////
18391 //////////////////// QCPBars
18392 ////////////////////////////////////////////////////////////////////////////////////////////////////
18395 \brief A plottable representing a bar chart in a plot.
18397 \image html QCPBars.png
18399 To plot data, assign it with the \ref setData or \ref addData functions.
18401 \section appearance Changing the appearance
18403 The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
18404 The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth.
18406 Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other
18407 (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear
18410 If you would like to group multiple QCPBars plottables together so they appear side by side as
18411 shown below, use QCPBarsGroup.
18413 \image html QCPBarsGroup.png
18415 \section usage Usage
18417 Like all data representing objects in QCustomPlot, the QCPBars is a plottable
18418 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
18419 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
18421 Usually, you first create an instance:
18422 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1
18423 add it to the customPlot with QCustomPlot::addPlottable:
18424 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2
18425 and then modify the properties of the newly created plottable, e.g.:
18426 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3
18429 /* start of documentation of inline functions */
18431 /*! \fn QCPBars *QCPBars::barBelow() const
18432 Returns the bars plottable that is directly below this bars plottable.
18433 If there is no such plottable, returns 0.
18435 \see barAbove, moveBelow, moveAbove
18438 /*! \fn QCPBars *QCPBars::barAbove() const
18439 Returns the bars plottable that is directly above this bars plottable.
18440 If there is no such plottable, returns 0.
18442 \see barBelow, moveBelow, moveAbove
18445 /* end of documentation of inline functions */
18448 Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
18449 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
18450 the same orientation. If either of these restrictions is violated, a corresponding message is
18451 printed to the debug output (qDebug), the construction is not aborted, though.
18453 The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
18454 then takes ownership of the bar chart.
18456 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18457 QCPAbstractPlottable(keyAxis, valueAxis),
18458 mData(new QCPBarDataMap),
18460 mWidthType(wtPlotCoords),
18464 // modify inherited properties from abstract plottable:
18465 mPen.setColor(Qt::blue);
18466 mPen.setStyle(Qt::SolidLine);
18467 mBrush.setColor(QColor(40, 50, 255, 30));
18468 mBrush.setStyle(Qt::SolidPattern);
18469 mSelectedPen = mPen;
18470 mSelectedPen.setWidthF(2.5);
18471 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18472 mSelectedBrush = mBrush;
18475 QCPBars::~QCPBars()
18478 if (mBarBelow || mBarAbove)
18479 connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18484 Sets the width of the bars.
18486 How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...),
18487 depends on the currently set width type, see \ref setWidthType and \ref WidthType.
18489 void QCPBars::setWidth(double width)
18495 Sets how the width of the bars is defined. See the documentation of \ref WidthType for an
18496 explanation of the possible values for \a widthType.
18498 The default value is \ref wtPlotCoords.
18502 void QCPBars::setWidthType(QCPBars::WidthType widthType)
18504 mWidthType = widthType;
18508 Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref
18509 QCPBarsGroup::append.
18511 To remove this QCPBars from any group, set \a barsGroup to 0.
18513 void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
18515 // deregister at old group:
18517 mBarsGroup->unregisterBars(this);
18518 mBarsGroup = barsGroup;
18519 // register at new group:
18521 mBarsGroup->registerBars(this);
18525 Sets the base value of this bars plottable.
18527 The base value defines where on the value coordinate the bars start. How far the bars extend from
18528 the base value is given by their individual value data. For example, if the base value is set to
18529 1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at
18532 For stacked bars, only the base value of the bottom-most QCPBars has meaning.
18534 The default base value is 0.
18536 void QCPBars::setBaseValue(double baseValue)
18538 mBaseValue = baseValue;
18542 Replaces the current data with the provided \a data.
18544 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
18545 takes ownership of the passed data and replaces the internal data pointer with it. This is
18546 significantly faster than copying for large datasets.
18548 void QCPBars::setData(QCPBarDataMap *data, bool copy)
18552 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
18567 Replaces the current data with the provided points in \a key and \a value tuples. The
18568 provided vectors should have equal length. Else, the number of added points will be the size of
18569 the smallest vector.
18571 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
18574 int n = key.size();
18575 n = qMin(n, value.size());
18576 QCPBarData newData;
18577 for (int i=0; i<n; ++i)
18579 newData.key = key[i];
18580 newData.value = value[i];
18581 mData->insertMulti(newData.key, newData);
18586 Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
18587 below the bars of \a bars. The move target \a bars must use the same key and value axis as this
18590 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18591 has a bars object below itself, this bars object is inserted between the two. If this bars object
18592 is already between two other bars, the two other bars will be stacked on top of each other after
18595 To remove this bars plottable from any stacking, set \a bars to 0.
18597 \see moveBelow, barAbove, barBelow
18599 void QCPBars::moveBelow(QCPBars *bars)
18601 if (bars == this) return;
18602 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18604 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18607 // remove from stacking:
18608 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18609 // if new bar given, insert this bar below it:
18612 if (bars->mBarBelow)
18613 connectBars(bars->mBarBelow.data(), this);
18614 connectBars(this, bars);
18619 Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
18620 above the bars of \a bars. The move target \a bars must use the same key and value axis as this
18623 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18624 has a bars object above itself, this bars object is inserted between the two. If this bars object
18625 is already between two other bars, the two other bars will be stacked on top of each other after
18628 To remove this bars plottable from any stacking, set \a bars to 0.
18630 \see moveBelow, barBelow, barAbove
18632 void QCPBars::moveAbove(QCPBars *bars)
18634 if (bars == this) return;
18635 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18637 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18640 // remove from stacking:
18641 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18642 // if new bar given, insert this bar above it:
18645 if (bars->mBarAbove)
18646 connectBars(this, bars->mBarAbove.data());
18647 connectBars(bars, this);
18652 Adds the provided data points in \a dataMap to the current data.
18655 void QCPBars::addData(const QCPBarDataMap &dataMap)
18657 mData->unite(dataMap);
18661 Adds the provided single data point in \a data to the current data.
18664 void QCPBars::addData(const QCPBarData &data)
18666 mData->insertMulti(data.key, data);
18670 Adds the provided single data point as \a key and \a value tuple to the current data
18673 void QCPBars::addData(double key, double value)
18675 QCPBarData newData;
18677 newData.value = value;
18678 mData->insertMulti(newData.key, newData);
18682 Adds the provided data points as \a key and \a value tuples to the current data.
18685 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
18687 int n = keys.size();
18688 n = qMin(n, values.size());
18689 QCPBarData newData;
18690 for (int i=0; i<n; ++i)
18692 newData.key = keys[i];
18693 newData.value = values[i];
18694 mData->insertMulti(newData.key, newData);
18699 Removes all data points with key smaller than \a key.
18700 \see addData, clearData
18702 void QCPBars::removeDataBefore(double key)
18704 QCPBarDataMap::iterator it = mData->begin();
18705 while (it != mData->end() && it.key() < key)
18706 it = mData->erase(it);
18710 Removes all data points with key greater than \a key.
18711 \see addData, clearData
18713 void QCPBars::removeDataAfter(double key)
18715 if (mData->isEmpty()) return;
18716 QCPBarDataMap::iterator it = mData->upperBound(key);
18717 while (it != mData->end())
18718 it = mData->erase(it);
18722 Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
18723 greater or equal to \a toKey, the function does nothing. To remove a single data point with known
18724 key, use \ref removeData(double key).
18726 \see addData, clearData
18728 void QCPBars::removeData(double fromKey, double toKey)
18730 if (fromKey >= toKey || mData->isEmpty()) return;
18731 QCPBarDataMap::iterator it = mData->upperBound(fromKey);
18732 QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
18733 while (it != itEnd)
18734 it = mData->erase(it);
18739 Removes a single data point at \a key. If the position is not known with absolute precision,
18740 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
18741 around the suspected position, depeding on the precision with which the key is known.
18743 \see addData, clearData
18745 void QCPBars::removeData(double key)
18747 mData->remove(key);
18751 Removes all data points.
18752 \see removeData, removeDataAfter, removeDataBefore
18754 void QCPBars::clearData()
18759 /* inherits documentation from base class */
18760 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18763 if (onlySelectable && !mSelectable)
18765 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18767 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18769 QCPBarDataMap::ConstIterator it;
18770 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
18772 if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
18773 return mParentPlot->selectionTolerance()*0.99;
18779 /* inherits documentation from base class */
18780 void QCPBars::draw(QCPPainter *painter)
18782 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18783 if (mData->isEmpty()) return;
18785 QCPBarDataMap::const_iterator it, lower, upperEnd;
18786 getVisibleDataBounds(lower, upperEnd);
18787 for (it = lower; it != upperEnd; ++it)
18789 // check data validity if flag set:
18790 #ifdef QCUSTOMPLOT_CHECK_DATA
18791 if (QCP::isInvalidData(it.value().key, it.value().value))
18792 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
18794 QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
18796 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
18798 applyFillAntialiasingHint(painter);
18799 painter->setPen(Qt::NoPen);
18800 painter->setBrush(mainBrush());
18801 painter->drawPolygon(barPolygon);
18804 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
18806 applyDefaultAntialiasingHint(painter);
18807 painter->setPen(mainPen());
18808 painter->setBrush(Qt::NoBrush);
18809 painter->drawPolyline(barPolygon);
18814 /* inherits documentation from base class */
18815 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18817 // draw filled rect:
18818 applyDefaultAntialiasingHint(painter);
18819 painter->setBrush(mBrush);
18820 painter->setPen(mPen);
18821 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18822 r.moveCenter(rect.center());
18823 painter->drawRect(r);
18828 called by \ref draw to determine which data (key) range is visible at the current key axis range
18829 setting, so only that needs to be processed. It also takes into account the bar width.
18831 \a lower returns an iterator to the lowest data point that needs to be taken into account when
18832 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
18833 lower may still be just outside the visible range.
18835 \a upperEnd returns an iterator one higher than the highest visible data point. Same as before, \a
18836 upperEnd may also lie just outside of the visible range.
18838 if the bars plottable contains no data, both \a lower and \a upperEnd point to constEnd.
18840 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
18842 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
18843 if (mData->isEmpty())
18845 lower = mData->constEnd();
18846 upperEnd = mData->constEnd();
18850 // get visible data range as QMap iterators
18851 lower = mData->lowerBound(mKeyAxis.data()->range().lower);
18852 upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
18853 double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
18854 double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
18855 bool isVisible = false;
18856 // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
18857 QCPBarDataMap::const_iterator it = lower;
18858 while (it != mData->constBegin())
18861 QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
18862 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18863 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
18864 else // keyaxis is vertical
18865 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
18871 // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
18873 while (it != mData->constEnd())
18875 QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
18876 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18877 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
18878 else // keyaxis is vertical
18879 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
18890 Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
18891 and shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref
18894 QPolygonF QCPBars::getBarPolygon(double key, double value) const
18896 QCPAxis *keyAxis = mKeyAxis.data();
18897 QCPAxis *valueAxis = mValueAxis.data();
18898 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
18901 double lowerPixelWidth, upperPixelWidth;
18902 getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
18903 double base = getStackedBaseValue(key, value >= 0);
18904 double basePixel = valueAxis->coordToPixel(base);
18905 double valuePixel = valueAxis->coordToPixel(base+value);
18906 double keyPixel = keyAxis->coordToPixel(key);
18908 keyPixel += mBarsGroup->keyPixelOffset(this, key);
18909 if (keyAxis->orientation() == Qt::Horizontal)
18911 result << QPointF(keyPixel+lowerPixelWidth, basePixel);
18912 result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
18913 result << QPointF(keyPixel+upperPixelWidth, valuePixel);
18914 result << QPointF(keyPixel+upperPixelWidth, basePixel);
18917 result << QPointF(basePixel, keyPixel+lowerPixelWidth);
18918 result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
18919 result << QPointF(valuePixel, keyPixel+upperPixelWidth);
18920 result << QPointF(basePixel, keyPixel+upperPixelWidth);
18927 This function is used to determine the width of the bar at coordinate \a key, according to the
18928 specified width (\ref setWidth) and width type (\ref setWidthType).
18930 The output parameters \a lower and \a upper return the number of pixels the bar extends to lower
18931 and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a
18932 lower is negative and \a upper positive).
18934 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
18936 switch (mWidthType)
18940 upper = mWidth*0.5;
18942 if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18943 qSwap(lower, upper);
18946 case wtAxisRectRatio:
18948 if (mKeyAxis && mKeyAxis.data()->axisRect())
18950 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18951 upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
18953 upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
18955 if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18956 qSwap(lower, upper);
18958 qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
18965 double keyPixel = mKeyAxis.data()->coordToPixel(key);
18966 upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
18967 lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
18968 // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
18969 // coordinate transform which includes range direction
18971 qDebug() << Q_FUNC_INFO << "No key axis defined";
18979 This function is called to find at which value to start drawing the base of a bar at \a key, when
18980 it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
18982 positive and negative bars are separated per stack (positive are stacked above baseValue upwards,
18983 negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the
18984 bar for which we need the base value is negative, set \a positive to false.
18986 double QCPBars::getStackedBaseValue(double key, bool positive) const
18990 double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
18991 // find bars of mBarBelow that are approximately at key and find largest one:
18992 double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
18995 QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
18996 QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
18997 while (it != itEnd)
18999 if ((positive && it.value().value > max) ||
19000 (!positive && it.value().value < max))
19001 max = it.value().value;
19004 // recurse down the bar-stack to find the total height:
19005 return max + mBarBelow.data()->getStackedBaseValue(key, positive);
19012 Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s)
19013 currently above lower and below upper will become disconnected to lower/upper.
19015 If lower is zero, upper will be disconnected at the bottom.
19016 If upper is zero, lower will be disconnected at the top.
19018 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
19020 if (!lower && !upper) return;
19022 if (!lower) // disconnect upper at bottom
19024 // disconnect old bar below upper:
19025 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19026 upper->mBarBelow.data()->mBarAbove = 0;
19027 upper->mBarBelow = 0;
19028 } else if (!upper) // disconnect lower at top
19030 // disconnect old bar above lower:
19031 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19032 lower->mBarAbove.data()->mBarBelow = 0;
19033 lower->mBarAbove = 0;
19034 } else // connect lower and upper
19036 // disconnect old bar above lower:
19037 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19038 lower->mBarAbove.data()->mBarBelow = 0;
19039 // disconnect old bar below upper:
19040 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19041 upper->mBarBelow.data()->mBarAbove = 0;
19042 lower->mBarAbove = upper;
19043 upper->mBarBelow = lower;
19047 /* inherits documentation from base class */
19048 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19051 bool haveLower = false;
19052 bool haveUpper = false;
19055 QCPBarDataMap::const_iterator it = mData->constBegin();
19056 while (it != mData->constEnd())
19058 current = it.value().key;
19059 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19061 if (current < range.lower || !haveLower)
19063 range.lower = current;
19066 if (current > range.upper || !haveUpper)
19068 range.upper = current;
19074 // determine exact range of bars by including bar width and barsgroup offset:
19075 if (haveLower && mKeyAxis)
19077 double lowerPixelWidth, upperPixelWidth, keyPixel;
19078 getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19079 keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19081 keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19082 range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19084 if (haveUpper && mKeyAxis)
19086 double lowerPixelWidth, upperPixelWidth, keyPixel;
19087 getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19088 keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19090 keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19091 range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19093 foundRange = haveLower && haveUpper;
19097 /* inherits documentation from base class */
19098 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19101 range.lower = mBaseValue;
19102 range.upper = mBaseValue;
19103 bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19104 bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19107 QCPBarDataMap::const_iterator it = mData->constBegin();
19108 while (it != mData->constEnd())
19110 current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19111 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19113 if (current < range.lower || !haveLower)
19115 range.lower = current;
19118 if (current > range.upper || !haveUpper)
19120 range.upper = current;
19127 foundRange = true; // return true because bar charts always have the 0-line visible
19132 ////////////////////////////////////////////////////////////////////////////////////////////////////
19133 //////////////////// QCPStatisticalBox
19134 ////////////////////////////////////////////////////////////////////////////////////////////////////
19136 /*! \class QCPStatisticalBox
19137 \brief A plottable representing a single statistical box in a plot.
19139 \image html QCPStatisticalBox.png
19141 To plot data, assign it with the individual parameter functions or use \ref setData to set all
19142 parameters at once. The individual functions are:
19143 \li \ref setMinimum
19144 \li \ref setLowerQuartile
19146 \li \ref setUpperQuartile
19147 \li \ref setMaximum
19149 Additionally you can define a list of outliers, drawn as scatter datapoints:
19150 \li \ref setOutliers
19152 \section appearance Changing the appearance
19154 The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You may change
19155 the width of the box with \ref setWidth in plot coordinates (not pixels).
19157 Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
19158 setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
19159 (maximum) and bottom (minimum).
19161 The median indicator line has its own pen, \ref setMedianPen.
19163 If the whisker backbone pen is changed, make sure to set the capStyle to Qt::FlatCap. Else, the
19164 backbone line might exceed the whisker bars by a few pixels due to the pen cap being not
19167 The Outlier data points are drawn as normal scatter points. Their look can be controlled with
19168 \ref setOutlierStyle
19170 \section usage Usage
19172 Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable.
19173 Usually, you first create an instance and add it to the customPlot:
19174 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1
19175 and then modify the properties of the newly created plottable, e.g.:
19176 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2
19180 Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
19181 value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
19182 not have the same orientation. If either of these restrictions is violated, a corresponding
19183 message is printed to the debug output (qDebug), the construction is not aborted, though.
19185 The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
19186 QCustomPlot then takes ownership of the statistical box.
19188 QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
19189 QCPAbstractPlottable(keyAxis, valueAxis),
19197 setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
19198 setWhiskerWidth(0.2);
19201 setPen(QPen(Qt::black));
19202 setSelectedPen(QPen(Qt::blue, 2.5));
19203 setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19204 setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19205 setWhiskerBarPen(QPen(Qt::black));
19206 setBrush(Qt::NoBrush);
19207 setSelectedBrush(Qt::NoBrush);
19211 Sets the key coordinate of the statistical box.
19213 void QCPStatisticalBox::setKey(double key)
19219 Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
19220 whisker, typically the minimum measurement of the sample that's not considered an outlier.
19222 \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19224 void QCPStatisticalBox::setMinimum(double value)
19230 Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
19231 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19232 sample, they contain 50% of the sample data.
19234 \see setUpperQuartile, setPen, setBrush, setWidth
19236 void QCPStatisticalBox::setLowerQuartile(double value)
19238 mLowerQuartile = value;
19242 Sets the parameter "median" of the statistical box plot. This is the value of the median mark
19243 inside the quartile box. The median separates the sample data in half (50% of the sample data is
19244 below/above the median).
19248 void QCPStatisticalBox::setMedian(double value)
19254 Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
19255 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19256 sample, they contain 50% of the sample data.
19258 \see setLowerQuartile, setPen, setBrush, setWidth
19260 void QCPStatisticalBox::setUpperQuartile(double value)
19262 mUpperQuartile = value;
19266 Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
19267 whisker, typically the maximum measurement of the sample that's not considered an outlier.
19269 \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19271 void QCPStatisticalBox::setMaximum(double value)
19277 Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
19278 that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
19279 and displayed as such.
19281 \see setOutlierStyle
19283 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19285 mOutliers = values;
19289 Sets all parameters of the statistical box plot at once.
19291 \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
19293 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19296 setMinimum(minimum);
19297 setLowerQuartile(lowerQuartile);
19299 setUpperQuartile(upperQuartile);
19300 setMaximum(maximum);
19304 Sets the width of the box in key coordinates.
19306 \see setWhiskerWidth
19308 void QCPStatisticalBox::setWidth(double width)
19314 Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
19318 void QCPStatisticalBox::setWhiskerWidth(double width)
19320 mWhiskerWidth = width;
19324 Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
19326 Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker backbone from reaching
19327 a few pixels past the whisker bars, when using a non-zero pen width.
19329 \see setWhiskerBarPen
19331 void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
19337 Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
19338 each end of the whisker backbone).
19342 void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
19344 mWhiskerBarPen = pen;
19348 Sets the pen used for drawing the median indicator line inside the statistical box.
19350 void QCPStatisticalBox::setMedianPen(const QPen &pen)
19356 Sets the appearance of the outlier data points.
19360 void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)
19362 mOutlierStyle = style;
19365 /* inherits documentation from base class */
19366 void QCPStatisticalBox::clearData()
19368 setOutliers(QVector<double>());
19371 setLowerQuartile(0);
19373 setUpperQuartile(0);
19377 /* inherits documentation from base class */
19378 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19381 if (onlySelectable && !mSelectable)
19383 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19385 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19387 double posKey, posValue;
19388 pixelsToCoords(pos, posKey, posValue);
19390 QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19391 QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19392 if (keyRange.contains(posKey) && valueRange.contains(posValue))
19393 return mParentPlot->selectionTolerance()*0.99;
19395 // min/max whiskers:
19396 if (QCPRange(mMinimum, mMaximum).contains(posValue))
19397 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
19402 /* inherits documentation from base class */
19403 void QCPStatisticalBox::draw(QCPPainter *painter)
19405 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19407 // check data validity if flag set:
19408 #ifdef QCUSTOMPLOT_CHECK_DATA
19409 if (QCP::isInvalidData(mKey, mMedian) ||
19410 QCP::isInvalidData(mLowerQuartile, mUpperQuartile) ||
19411 QCP::isInvalidData(mMinimum, mMaximum))
19412 qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19413 for (int i=0; i<mOutliers.size(); ++i)
19414 if (QCP::isInvalidData(mOutliers.at(i)))
19415 qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19418 QRectF quartileBox;
19419 drawQuartileBox(painter, &quartileBox);
19422 painter->setClipRect(quartileBox, Qt::IntersectClip);
19423 drawMedian(painter);
19424 painter->restore();
19426 drawWhiskers(painter);
19427 drawOutliers(painter);
19430 /* inherits documentation from base class */
19431 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19433 // draw filled rect:
19434 applyDefaultAntialiasingHint(painter);
19435 painter->setPen(mPen);
19436 painter->setBrush(mBrush);
19437 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19438 r.moveCenter(rect.center());
19439 painter->drawRect(r);
19444 Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
19445 coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
19446 the median doesn't draw outside the quartile box).
19448 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19451 box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19452 box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19453 applyDefaultAntialiasingHint(painter);
19454 painter->setPen(mainPen());
19455 painter->setBrush(mainBrush());
19456 painter->drawRect(box);
19458 *quartileBox = box;
19463 Draws the median line inside the quartile box.
19465 void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
19468 medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19469 medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19470 applyDefaultAntialiasingHint(painter);
19471 painter->setPen(mMedianPen);
19472 painter->drawLine(medianLine);
19477 Draws both whisker backbones and bars.
19479 void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
19481 QLineF backboneMin, backboneMax, barMin, barMax;
19482 backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19483 backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19484 barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
19485 barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
19486 applyErrorBarsAntialiasingHint(painter);
19487 painter->setPen(mWhiskerPen);
19488 painter->drawLine(backboneMin);
19489 painter->drawLine(backboneMax);
19490 painter->setPen(mWhiskerBarPen);
19491 painter->drawLine(barMin);
19492 painter->drawLine(barMax);
19497 Draws the outlier scatter points.
19499 void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
19501 applyScattersAntialiasingHint(painter);
19502 mOutlierStyle.applyTo(painter, mPen);
19503 for (int i=0; i<mOutliers.size(); ++i)
19504 mOutlierStyle.drawShape(painter, coordsToPixels(mKey, mOutliers.at(i)));
19507 /* inherits documentation from base class */
19508 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19511 if (inSignDomain == sdBoth)
19513 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19514 } else if (inSignDomain == sdNegative)
19516 if (mKey+mWidth*0.5 < 0)
19517 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19519 return QCPRange(mKey-mWidth*0.5, mKey);
19522 foundRange = false;
19525 } else if (inSignDomain == sdPositive)
19527 if (mKey-mWidth*0.5 > 0)
19528 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19530 return QCPRange(mKey, mKey+mWidth*0.5);
19533 foundRange = false;
19537 foundRange = false;
19541 /* inherits documentation from base class */
19542 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19544 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
19545 values.reserve(mOutliers.size() + 5);
19546 values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
19547 values << mOutliers;
19548 // go through values and find the ones in legal range:
19549 bool haveUpper = false;
19550 bool haveLower = false;
19553 for (int i=0; i<values.size(); ++i)
19555 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
19556 (inSignDomain == sdPositive && values.at(i) > 0) ||
19557 (inSignDomain == sdBoth))
19559 if (values.at(i) > upper || !haveUpper)
19561 upper = values.at(i);
19564 if (values.at(i) < lower || !haveLower)
19566 lower = values.at(i);
19571 // return the bounds if we found some sensible values:
19572 if (haveLower && haveUpper)
19575 return QCPRange(lower, upper);
19576 } else // might happen if all values are in other sign domain
19578 foundRange = false;
19584 ////////////////////////////////////////////////////////////////////////////////////////////////////
19585 //////////////////// QCPColorMapData
19586 ////////////////////////////////////////////////////////////////////////////////////////////////////
19588 /*! \class QCPColorMapData
19589 \brief Holds the two-dimensional data of a QCPColorMap plottable.
19591 This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref
19592 QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a
19593 color, depending on the value.
19595 The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize).
19596 Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref
19597 setKeyRange, \ref setValueRange).
19599 The data cells can be accessed in two ways: They can be directly addressed by an integer index
19600 with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot
19601 coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are
19602 provided by the functions \ref coordToCell and \ref cellToCoord.
19604 This class also buffers the minimum and maximum values that are in the data set, to provide
19605 QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value
19606 that is greater than the current maximum increases this maximum to the new value. However,
19607 setting the cell that currently holds the maximum value to a smaller value doesn't decrease the
19608 maximum again, because finding the true new maximum would require going through the entire data
19609 array, which might be time consuming. The same holds for the data minimum. This functionality is
19610 given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the
19611 true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience
19612 parameter \a recalculateDataBounds which may be set to true to automatically call \ref
19613 recalculateDataBounds internally.
19616 /* start of documentation of inline functions */
19618 /*! \fn bool QCPColorMapData::isEmpty() const
19620 Returns whether this instance carries no data. This is equivalent to having a size where at least
19621 one of the dimensions is 0 (see \ref setSize).
19624 /* end of documentation of inline functions */
19627 Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction
19628 and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap
19629 at the coordinates \a keyRange and \a valueRange.
19631 \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
19633 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
19636 mKeyRange(keyRange),
19637 mValueRange(valueRange),
19640 mDataModified(true)
19642 setSize(keySize, valueSize);
19646 QCPColorMapData::~QCPColorMapData()
19653 Constructs a new QCPColorMapData instance copying the data and range of \a other.
19655 QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
19660 mDataModified(true)
19666 Overwrites this color map data instance with the data stored in \a other.
19668 QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
19670 if (&other != this)
19672 const int keySize = other.keySize();
19673 const int valueSize = other.valueSize();
19674 setSize(keySize, valueSize);
19675 setRange(other.keyRange(), other.valueRange());
19677 memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
19678 mDataBounds = other.mDataBounds;
19679 mDataModified = true;
19684 /* undocumented getter */
19685 double QCPColorMapData::data(double key, double value)
19687 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19688 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19689 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19690 return mData[valueCell*mKeySize + keyCell];
19695 /* undocumented getter */
19696 double QCPColorMapData::cell(int keyIndex, int valueIndex)
19698 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19699 return mData[valueIndex*mKeySize + keyIndex];
19705 Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in
19706 the value dimension.
19708 The current data is discarded and the map cells are set to 0, unless the map had already the
19711 Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref
19712 isEmpty returns true.
19714 \see setRange, setKeySize, setValueSize
19716 void QCPColorMapData::setSize(int keySize, int valueSize)
19718 if (keySize != mKeySize || valueSize != mValueSize)
19720 mKeySize = keySize;
19721 mValueSize = valueSize;
19724 mIsEmpty = mKeySize == 0 || mValueSize == 0;
19727 #ifdef __EXCEPTIONS
19728 try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
19730 mData = new double[mKeySize*mValueSize];
19731 #ifdef __EXCEPTIONS
19732 } catch (...) { mData = 0; }
19737 qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
19740 mDataModified = true;
19745 Resizes the data array to have \a keySize cells in the key dimension.
19747 The current data is discarded and the map cells are set to 0, unless the map had already the
19750 Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true.
19752 \see setKeyRange, setSize, setValueSize
19754 void QCPColorMapData::setKeySize(int keySize)
19756 setSize(keySize, mValueSize);
19760 Resizes the data array to have \a valueSize cells in the value dimension.
19762 The current data is discarded and the map cells are set to 0, unless the map had already the
19765 Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true.
19767 \see setValueRange, setSize, setKeySize
19769 void QCPColorMapData::setValueSize(int valueSize)
19771 setSize(mKeySize, valueSize);
19775 Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area
19776 covered by the color map in plot coordinates.
19778 The outer cells will be centered on the range boundaries given to this function. For example, if
19779 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19780 be cells centered on the key coordinates 2, 2.5 and 3.
19784 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
19786 setKeyRange(keyRange);
19787 setValueRange(valueRange);
19791 Sets the coordinate range the data shall be distributed over in the key dimension. Together with
19792 the value range, This defines the rectangular area covered by the color map in plot coordinates.
19794 The outer cells will be centered on the range boundaries given to this function. For example, if
19795 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19796 be cells centered on the key coordinates 2, 2.5 and 3.
19798 \see setRange, setValueRange, setSize
19800 void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
19802 mKeyRange = keyRange;
19806 Sets the coordinate range the data shall be distributed over in the value dimension. Together with
19807 the key range, This defines the rectangular area covered by the color map in plot coordinates.
19809 The outer cells will be centered on the range boundaries given to this function. For example, if
19810 the value size (\ref setValueSize) is 3 and \a valueRange is set to <tt>QCPRange(2, 3)</tt> there
19811 will be cells centered on the value coordinates 2, 2.5 and 3.
19813 \see setRange, setKeyRange, setSize
19815 void QCPColorMapData::setValueRange(const QCPRange &valueRange)
19817 mValueRange = valueRange;
19821 Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a
19824 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19825 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19826 you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
19827 determine the cell index. Rather directly access the cell index with \ref
19828 QCPColorMapData::setCell.
19830 \see setCell, setRange
19832 void QCPColorMapData::setData(double key, double value, double z)
19834 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19835 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19836 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19838 mData[valueCell*mKeySize + keyCell] = z;
19839 if (z < mDataBounds.lower)
19840 mDataBounds.lower = z;
19841 if (z > mDataBounds.upper)
19842 mDataBounds.upper = z;
19843 mDataModified = true;
19848 Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices
19849 enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see
19852 In the standard plot configuration (horizontal key axis and vertical value axis, both not
19853 range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with
19854 indices (keySize-1, valueSize-1) is in the top right corner of the color map.
19856 \see setData, setSize
19858 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
19860 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19862 mData[valueIndex*mKeySize + keyIndex] = z;
19863 if (z < mDataBounds.lower)
19864 mDataBounds.lower = z;
19865 if (z > mDataBounds.upper)
19866 mDataBounds.upper = z;
19867 mDataModified = true;
19872 Goes through the data and updates the buffered minimum and maximum data values.
19874 Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange
19875 and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten
19876 with a smaller or larger value respectively, since the buffered maximum/minimum values have been
19877 updated the last time. Why this is the case is explained in the class description (\ref
19880 Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a
19881 recalculateDataBounds for convenience. Setting this to true will call this method for you, before
19884 void QCPColorMapData::recalculateDataBounds()
19886 if (mKeySize > 0 && mValueSize > 0)
19888 double minHeight = mData[0];
19889 double maxHeight = mData[0];
19890 const int dataCount = mValueSize*mKeySize;
19891 for (int i=0; i<dataCount; ++i)
19893 if (mData[i] > maxHeight)
19894 maxHeight = mData[i];
19895 if (mData[i] < minHeight)
19896 minHeight = mData[i];
19898 mDataBounds.lower = minHeight;
19899 mDataBounds.upper = maxHeight;
19904 Frees the internal data memory.
19906 This is equivalent to calling \ref setSize "setSize(0, 0)".
19908 void QCPColorMapData::clear()
19914 Sets all cells to the value \a z.
19916 void QCPColorMapData::fill(double z)
19918 const int dataCount = mValueSize*mKeySize;
19919 for (int i=0; i<dataCount; ++i)
19921 mDataBounds = QCPRange(z, z);
19922 mDataModified = true;
19926 Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData
19927 instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a
19930 The retrieved key/value cell indices can then be used for example with \ref setCell.
19932 If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
19935 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19936 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19937 you shouldn't use the \ref QCPColorMapData::coordToCell method as it uses a linear transformation to
19938 determine the cell index.
19940 \see cellToCoord, QCPAxis::coordToPixel
19942 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
19945 *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19947 *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19951 Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData
19952 instance. The resulting coordinates are returned via the output parameters \a key and \a
19955 If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
19958 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19959 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19960 you shouldn't use the \ref QCPColorMapData::cellToCoord method as it uses a linear transformation to
19961 determine the cell index.
19963 \see coordToCell, QCPAxis::pixelToCoord
19965 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
19968 *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
19970 *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
19974 ////////////////////////////////////////////////////////////////////////////////////////////////////
19975 //////////////////// QCPColorMap
19976 ////////////////////////////////////////////////////////////////////////////////////////////////////
19978 /*! \class QCPColorMap
19979 \brief A plottable representing a two-dimensional color map in a plot.
19981 \image html QCPColorMap.png
19983 The data is stored in the class \ref QCPColorMapData, which can be accessed via the data()
19986 A color map has three dimensions to represent a data point: The \a key dimension, the \a value
19987 dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value
19988 correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QCPColorMap
19989 constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a
19992 Set the number of points (or \a cells) in the key/value dimension via \ref
19993 QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is
19994 specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range
19995 boundary and the last cell will be centered on the upper range boundary. The data can be set by
19996 either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via
19997 their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer
19998 setCell, since it doesn't need to do any coordinate transformation and thus performs a bit
20001 The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)
20002 key and value axes.
20004 To show the user which colors correspond to which \a data values, a \ref QCPColorScale is
20005 typically placed to the right of the axis rect. See the documentation there for details on how to
20006 add and use a color scale.
20008 \section appearance Changing the appearance
20010 The central part of the appearance is the color gradient, which can be specified via \ref
20011 setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
20014 The \a data range that is mapped to the colors of the gradient can be specified with \ref
20015 setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
20018 \section usage Usage
20020 Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable
20021 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
20022 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
20024 Usually, you first create an instance and add it to the customPlot:
20025 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1
20026 and then modify the properties of the newly created color map, e.g.:
20027 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2
20029 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
20030 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
20031 you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
20032 determine the cell index. Rather directly access the cell index with \ref
20033 QCPColorMapData::setCell.
20036 /* start documentation of inline functions */
20038 /*! \fn QCPColorMapData *QCPColorMap::data() const
20040 Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to
20041 modify data points (cells) and the color map key/value range.
20046 /* end documentation of inline functions */
20048 /* start documentation of signals */
20050 /*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
20052 This signal is emitted when the data range changes.
20057 /*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
20059 This signal is emitted when the data scale type changes.
20061 \see setDataScaleType
20064 /*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
20066 This signal is emitted when the gradient changes.
20071 /* end documentation of signals */
20074 Constructs a color map with the specified \a keyAxis and \a valueAxis.
20076 The constructed QCPColorMap can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20077 then takes ownership of the color map.
20079 QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20080 QCPAbstractPlottable(keyAxis, valueAxis),
20081 mDataScaleType(QCPAxis::stLinear),
20082 mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20083 mInterpolate(true),
20084 mTightBoundary(false),
20085 mMapImageInvalidated(true)
20089 QCPColorMap::~QCPColorMap()
20095 Replaces the current \ref data with the provided \a data.
20097 If \a copy is set to true, the \a data object will only be copied. if false, the color map
20098 takes ownership of the passed data and replaces the internal data pointer with it. This is
20099 significantly faster than copying for large datasets.
20101 void QCPColorMap::setData(QCPColorMapData *data, bool copy)
20103 if (mMapData == data)
20105 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20116 mMapImageInvalidated = true;
20120 Sets the data range of this color map to \a dataRange. The data range defines which data values
20121 are mapped to the color gradient.
20123 To make the data range span the full range of the data set, use \ref rescaleDataRange.
20125 \see QCPColorScale::setDataRange
20127 void QCPColorMap::setDataRange(const QCPRange &dataRange)
20129 if (!QCPRange::validRange(dataRange)) return;
20130 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20132 if (mDataScaleType == QCPAxis::stLogarithmic)
20133 mDataRange = dataRange.sanitizedForLogScale();
20135 mDataRange = dataRange.sanitizedForLinScale();
20136 mMapImageInvalidated = true;
20137 emit dataRangeChanged(mDataRange);
20142 Sets whether the data is correlated with the color gradient linearly or logarithmically.
20144 \see QCPColorScale::setDataScaleType
20146 void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
20148 if (mDataScaleType != scaleType)
20150 mDataScaleType = scaleType;
20151 mMapImageInvalidated = true;
20152 emit dataScaleTypeChanged(mDataScaleType);
20153 if (mDataScaleType == QCPAxis::stLogarithmic)
20154 setDataRange(mDataRange.sanitizedForLogScale());
20159 Sets the color gradient that is used to represent the data. For more details on how to create an
20160 own gradient or use one of the preset gradients, see \ref QCPColorGradient.
20162 The colors defined by the gradient will be used to represent data values in the currently set
20163 data range, see \ref setDataRange. Data points that are outside this data range will either be
20164 colored uniformly with the respective gradient boundary color, or the gradient will repeat,
20165 depending on \ref QCPColorGradient::setPeriodic.
20167 \see QCPColorScale::setGradient
20169 void QCPColorMap::setGradient(const QCPColorGradient &gradient)
20171 if (mGradient != gradient)
20173 mGradient = gradient;
20174 mMapImageInvalidated = true;
20175 emit gradientChanged(mGradient);
20180 Sets whether the color map image shall use bicubic interpolation when displaying the color map
20181 shrinked or expanded, and not at a 1:1 pixel-to-data scale.
20183 \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled"
20185 void QCPColorMap::setInterpolate(bool enabled)
20187 mInterpolate = enabled;
20188 mMapImageInvalidated = true; // because oversampling factors might need to change
20192 Sets whether the outer most data rows and columns are clipped to the specified key and value
20193 range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange).
20195 if \a enabled is set to false, the data points at the border of the color map are drawn with the
20196 same width and height as all other data points. Since the data points are represented by
20197 rectangles of one color centered on the data coordinate, this means that the shown color map
20198 extends by half a data point over the specified key/value range in each direction.
20200 \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled"
20202 void QCPColorMap::setTightBoundary(bool enabled)
20204 mTightBoundary = enabled;
20208 Associates the color scale \a colorScale with this color map.
20210 This means that both the color scale and the color map synchronize their gradient, data range and
20211 data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps
20212 can be associated with one single color scale. This causes the color maps to also synchronize
20213 those properties, via the mutual color scale.
20215 This function causes the color map to adopt the current color gradient, data range and data scale
20216 type of \a colorScale. After this call, you may change these properties at either the color map
20217 or the color scale, and the setting will be applied to both.
20219 Pass 0 as \a colorScale to disconnect the color scale from this color map again.
20221 void QCPColorMap::setColorScale(QCPColorScale *colorScale)
20223 if (mColorScale) // unconnect signals from old color scale
20225 disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20226 disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20227 disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20228 disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20229 disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20230 disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20232 mColorScale = colorScale;
20233 if (mColorScale) // connect signals to new color scale
20235 setGradient(mColorScale.data()->gradient());
20236 setDataRange(mColorScale.data()->dataRange());
20237 setDataScaleType(mColorScale.data()->dataScaleType());
20238 connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20239 connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20240 connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20241 connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20242 connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20243 connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20248 Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the
20249 current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods,
20250 only for the third data dimension of the color map.
20252 The minimum and maximum values of the data set are buffered in the internal QCPColorMapData
20253 instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref
20254 QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For
20255 performance reasons, however, they are only updated in an expanding fashion. So the buffered
20256 maximum can only increase and the buffered minimum can only decrease. In consequence, changes to
20257 the data that actually lower the maximum of the data set (by overwriting the cell holding the
20258 current maximum with a smaller value), aren't recognized and the buffered maximum overestimates
20259 the true maximum of the data set. The same happens for the buffered minimum. To recalculate the
20260 true minimum and maximum by explicitly looking at each cell, the method
20261 QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a
20262 recalculateDataBounds calls this method before setting the data range to the buffered minimum and
20267 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20269 if (recalculateDataBounds)
20270 mMapData->recalculateDataBounds();
20271 setDataRange(mMapData->dataBounds());
20275 Takes the current appearance of the color map and updates the legend icon, which is used to
20276 represent this color map in the legend (see \ref QCPLegend).
20278 The \a transformMode specifies whether the rescaling is done by a faster, low quality image
20279 scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm
20280 (Qt::SmoothTransformation).
20282 The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to
20283 the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured
20284 legend icon size, the thumb will be rescaled during drawing of the legend item.
20288 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20290 if (mMapImage.isNull() && !data()->isEmpty())
20291 updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20293 if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20295 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20296 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20297 mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20302 Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also
20303 resizes the map to 0x0 cells.
20305 void QCPColorMap::clearData()
20310 /* inherits documentation from base class */
20311 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20314 if (onlySelectable && !mSelectable)
20316 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20318 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20320 double posKey, posValue;
20321 pixelsToCoords(pos, posKey, posValue);
20322 if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20323 return mParentPlot->selectionTolerance()*0.99;
20330 Updates the internal map image buffer by going through the internal \ref QCPColorMapData and
20331 turning the data values into color pixels with \ref QCPColorGradient::colorize.
20333 This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image
20334 has been invalidated for a different reason (e.g. a change of the data range with \ref
20337 If the map cell count is low, the image created will be oversampled in order to avoid a
20338 QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images
20339 without smooth transform enabled. Accordingly, oversampling isn't performed if \ref
20340 setInterpolate is true.
20342 void QCPColorMap::updateMapImage()
20344 QCPAxis *keyAxis = mKeyAxis.data();
20345 if (!keyAxis) return;
20346 if (mMapData->isEmpty()) return;
20348 const int keySize = mMapData->keySize();
20349 const int valueSize = mMapData->valueSize();
20350 int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20351 int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
20353 // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
20354 if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
20355 mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32);
20356 else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
20357 mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32);
20359 QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
20360 if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20362 // resize undersampled map image to actual key/value cell sizes:
20363 if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
20364 mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
20365 else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
20366 mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
20367 localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
20368 } else if (!mUndersampledMapImage.isNull())
20369 mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
20371 const double *rawData = mMapData->mData;
20372 if (keyAxis->orientation() == Qt::Horizontal)
20374 const int lineCount = valueSize;
20375 const int rowCount = keySize;
20376 for (int line=0; line<lineCount; ++line)
20378 QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20379 mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20381 } else // keyAxis->orientation() == Qt::Vertical
20383 const int lineCount = keySize;
20384 const int rowCount = valueSize;
20385 for (int line=0; line<lineCount; ++line)
20387 QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
20388 mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20392 if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20394 if (keyAxis->orientation() == Qt::Horizontal)
20395 mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20397 mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20399 mMapData->mDataModified = false;
20400 mMapImageInvalidated = false;
20403 /* inherits documentation from base class */
20404 void QCPColorMap::draw(QCPPainter *painter)
20406 if (mMapData->isEmpty()) return;
20407 if (!mKeyAxis || !mValueAxis) return;
20408 applyDefaultAntialiasingHint(painter);
20410 if (mMapData->mDataModified || mMapImageInvalidated)
20413 // use buffer if painting vectorized (PDF):
20414 bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
20415 QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
20416 QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
20418 double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
20421 mapBufferTarget = painter->clipRegion().boundingRect();
20422 mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
20423 mapBuffer.fill(Qt::transparent);
20424 localPainter = new QCPPainter(&mapBuffer);
20425 localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
20426 localPainter->translate(-mapBufferTarget.topLeft());
20429 QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20430 coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
20431 // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
20432 double halfCellWidth = 0; // in pixels
20433 double halfCellHeight = 0; // in pixels
20434 if (keyAxis()->orientation() == Qt::Horizontal)
20436 if (mMapData->keySize() > 1)
20437 halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
20438 if (mMapData->valueSize() > 1)
20439 halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
20440 } else // keyAxis orientation is Qt::Vertical
20442 if (mMapData->keySize() > 1)
20443 halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
20444 if (mMapData->valueSize() > 1)
20445 halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
20447 imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
20448 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20449 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20450 bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20451 localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20452 QRegion clipBackup;
20453 if (mTightBoundary)
20455 clipBackup = localPainter->clipRegion();
20456 QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20457 coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
20458 localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
20460 localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20461 if (mTightBoundary)
20462 localPainter->setClipRegion(clipBackup);
20463 localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20465 if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
20467 delete localPainter;
20468 painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
20472 /* inherits documentation from base class */
20473 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20475 applyDefaultAntialiasingHint(painter);
20476 // draw map thumbnail:
20477 if (!mLegendIcon.isNull())
20479 QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20480 QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20481 iconRect.moveCenter(rect.center());
20482 painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20486 painter->setBrush(Qt::NoBrush);
20487 painter->setPen(Qt::black);
20488 painter->drawRect(rect.adjusted(1, 1, 0, 0));
20492 /* inherits documentation from base class */
20493 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20496 QCPRange result = mMapData->keyRange();
20497 result.normalize();
20498 if (inSignDomain == QCPAbstractPlottable::sdPositive)
20500 if (result.lower <= 0 && result.upper > 0)
20501 result.lower = result.upper*1e-3;
20502 else if (result.lower <= 0 && result.upper <= 0)
20503 foundRange = false;
20504 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20506 if (result.upper >= 0 && result.lower < 0)
20507 result.upper = result.lower*1e-3;
20508 else if (result.upper >= 0 && result.lower >= 0)
20509 foundRange = false;
20514 /* inherits documentation from base class */
20515 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20518 QCPRange result = mMapData->valueRange();
20519 result.normalize();
20520 if (inSignDomain == QCPAbstractPlottable::sdPositive)
20522 if (result.lower <= 0 && result.upper > 0)
20523 result.lower = result.upper*1e-3;
20524 else if (result.lower <= 0 && result.upper <= 0)
20525 foundRange = false;
20526 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20528 if (result.upper >= 0 && result.lower < 0)
20529 result.upper = result.lower*1e-3;
20530 else if (result.upper >= 0 && result.lower >= 0)
20531 foundRange = false;
20537 ////////////////////////////////////////////////////////////////////////////////////////////////////
20538 //////////////////// QCPFinancialData
20539 ////////////////////////////////////////////////////////////////////////////////////////////////////
20541 /*! \class QCPFinancialData
20542 \brief Holds the data of one single data point for QCPFinancial.
20544 The container for storing multiple data points is \ref QCPFinancialDataMap.
20546 The stored data is:
20547 \li \a key: coordinate on the key axis of this data point
20548 \li \a open: The opening value at the data point
20549 \li \a high: The high/maximum value at the data point
20550 \li \a low: The low/minimum value at the data point
20551 \li \a close: The closing value at the data point
20553 \see QCPFinancialDataMap
20557 Constructs a data point with key and all values set to zero.
20559 QCPFinancialData::QCPFinancialData() :
20569 Constructs a data point with the specified \a key and OHLC values.
20571 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20581 ////////////////////////////////////////////////////////////////////////////////////////////////////
20582 //////////////////// QCPFinancial
20583 ////////////////////////////////////////////////////////////////////////////////////////////////////
20585 /*! \class QCPFinancial
20586 \brief A plottable representing a financial stock chart
20588 \image html QCPFinancial.png
20590 This plottable represents time series data binned to certain intervals, mainly used for stock
20591 charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be
20592 set via \ref setChartStyle.
20594 The data is passed via \ref setData as a set of open/high/low/close values at certain keys
20595 (typically times). This means the data must be already binned appropriately. If data is only
20596 available as a series of values (e.g. \a price against \a time), you can use the static
20597 convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed
20600 The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and is given in plot
20601 key coordinates. A typical choice is to set it to (or slightly less than) one bin interval width.
20603 \section appearance Changing the appearance
20605 Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored,
20606 lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush).
20608 If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are
20609 represented with a different pen and brush than negative changes (\a close < \a open). These can
20610 be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref
20611 setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection
20612 however, the normal selected pen/brush (\ref setSelectedPen, \ref setSelectedBrush) is used,
20613 irrespective of whether the chart is single- or two-colored.
20617 /* start of documentation of inline functions */
20619 /*! \fn QCPFinancialDataMap *QCPFinancial::data() const
20621 Returns a pointer to the internal data storage of type \ref QCPFinancialDataMap. You may use it to
20622 directly manipulate the data, which may be more convenient and faster than using the regular \ref
20623 setData or \ref addData methods, in certain situations.
20626 /* end of documentation of inline functions */
20629 Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
20630 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
20631 the same orientation. If either of these restrictions is violated, a corresponding message is
20632 printed to the debug output (qDebug), the construction is not aborted, though.
20634 The constructed QCPFinancial can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20635 then takes ownership of the financial chart.
20637 QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20638 QCPAbstractPlottable(keyAxis, valueAxis),
20640 mChartStyle(csOhlc),
20642 mTwoColored(false),
20643 mBrushPositive(QBrush(QColor(210, 210, 255))),
20644 mBrushNegative(QBrush(QColor(255, 210, 210))),
20645 mPenPositive(QPen(QColor(10, 40, 180))),
20646 mPenNegative(QPen(QColor(180, 40, 10)))
20648 mData = new QCPFinancialDataMap;
20650 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
20651 setSelectedBrush(QBrush(QColor(80, 80, 255)));
20654 QCPFinancial::~QCPFinancial()
20660 Replaces the current data with the provided \a data.
20662 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
20663 takes ownership of the passed data and replaces the internal data pointer with it. This is
20664 significantly faster than copying for large datasets.
20666 Alternatively, you can also access and modify the plottable's data via the \ref data method, which
20667 returns a pointer to the internal \ref QCPFinancialDataMap.
20669 \see timeSeriesToOhlc
20671 void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy)
20675 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20690 Replaces the current data with the provided open/high/low/close data. The provided vectors should
20691 have equal length. Else, the number of added points will be the size of the smallest vector.
20693 \see timeSeriesToOhlc
20695 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20698 int n = key.size();
20699 n = qMin(n, open.size());
20700 n = qMin(n, high.size());
20701 n = qMin(n, low.size());
20702 n = qMin(n, close.size());
20703 for (int i=0; i<n; ++i)
20705 mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20710 Sets which representation style shall be used to display the OHLC data.
20712 void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)
20714 mChartStyle = style;
20718 Sets the width of the individual bars/candlesticks to \a width in plot key coordinates.
20720 A typical choice is to set it to (or slightly less than) one bin interval width.
20722 void QCPFinancial::setWidth(double width)
20728 Sets whether this chart shall contrast positive from negative trends per data point by using two
20729 separate colors to draw the respective bars/candlesticks.
20731 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20734 \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative
20736 void QCPFinancial::setTwoColored(bool twoColored)
20738 mTwoColored = twoColored;
20742 If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20743 of data points with a positive 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 setBrushNegative, setPenPositive, setPenNegative
20750 void QCPFinancial::setBrushPositive(const QBrush &brush)
20752 mBrushPositive = brush;
20756 If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20757 of data points with a negative trend (i.e. bars/candlesticks with close < open).
20759 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20762 \see setBrushPositive, setPenNegative, setPenPositive
20764 void QCPFinancial::setBrushNegative(const QBrush &brush)
20766 mBrushNegative = brush;
20770 If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20771 outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20773 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20776 \see setPenNegative, setBrushPositive, setBrushNegative
20778 void QCPFinancial::setPenPositive(const QPen &pen)
20780 mPenPositive = pen;
20784 If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20785 outlines of data points with a negative trend (i.e. bars/candlesticks with close < open).
20787 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20790 \see setPenPositive, setBrushNegative, setBrushPositive
20792 void QCPFinancial::setPenNegative(const QPen &pen)
20794 mPenNegative = pen;
20798 Adds the provided data points in \a dataMap to the current data.
20800 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20801 pointer to the internal \ref QCPFinancialDataMap.
20805 void QCPFinancial::addData(const QCPFinancialDataMap &dataMap)
20807 mData->unite(dataMap);
20812 Adds the provided single data point in \a data to the current data.
20814 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20815 pointer to the internal \ref QCPFinancialData.
20819 void QCPFinancial::addData(const QCPFinancialData &data)
20821 mData->insertMulti(data.key, data);
20826 Adds the provided single data point given by \a key, \a open, \a high, \a low, and \a close to
20829 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20830 pointer to the internal \ref QCPFinancialData.
20834 void QCPFinancial::addData(double key, double open, double high, double low, double close)
20836 mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
20841 Adds the provided open/high/low/close data to the current data.
20843 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20844 pointer to the internal \ref QCPFinancialData.
20848 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20850 int n = key.size();
20851 n = qMin(n, open.size());
20852 n = qMin(n, high.size());
20853 n = qMin(n, low.size());
20854 n = qMin(n, close.size());
20855 for (int i=0; i<n; ++i)
20857 mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20862 Removes all data points with keys smaller than \a key.
20864 \see addData, clearData
20866 void QCPFinancial::removeDataBefore(double key)
20868 QCPFinancialDataMap::iterator it = mData->begin();
20869 while (it != mData->end() && it.key() < key)
20870 it = mData->erase(it);
20874 Removes all data points with keys greater than \a key.
20876 \see addData, clearData
20878 void QCPFinancial::removeDataAfter(double key)
20880 if (mData->isEmpty()) return;
20881 QCPFinancialDataMap::iterator it = mData->upperBound(key);
20882 while (it != mData->end())
20883 it = mData->erase(it);
20887 Removes all data points with keys between \a fromKey and \a toKey. if \a fromKey is greater or
20888 equal to \a toKey, the function does nothing. To remove a single data point with known key, use
20889 \ref removeData(double key).
20891 \see addData, clearData
20893 void QCPFinancial::removeData(double fromKey, double toKey)
20895 if (fromKey >= toKey || mData->isEmpty()) return;
20896 QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
20897 QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
20898 while (it != itEnd)
20899 it = mData->erase(it);
20904 Removes a single data point at \a key. If the position is not known with absolute precision,
20905 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
20906 around the suspected position, depeding on the precision with which the key is known.
20908 \see addData, clearData
20910 void QCPFinancial::removeData(double key)
20912 mData->remove(key);
20916 Removes all data points.
20918 \see removeData, removeDataAfter, removeDataBefore
20920 void QCPFinancial::clearData()
20925 /* inherits documentation from base class */
20926 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20929 if (onlySelectable && !mSelectable)
20931 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20933 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20935 // get visible data range:
20936 QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20937 getVisibleDataBounds(lower, upper);
20938 if (lower == mData->constEnd() || upper == mData->constEnd())
20940 // perform select test according to configured style:
20941 switch (mChartStyle)
20943 case QCPFinancial::csOhlc:
20944 return ohlcSelectTest(pos, lower, upper+1); break;
20945 case QCPFinancial::csCandlestick:
20946 return candlestickSelectTest(pos, lower, upper+1); break;
20953 A convenience function that converts time series data (\a value against \a time) to OHLC binned
20954 data points. The return value can then be passed on to \ref setData.
20956 The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given.
20957 For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour
20958 each, set \a timeBinSize to 3600.
20960 \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The
20961 value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys.
20962 It merely defines the mathematical offset/phase of the bins that will be used to process the
20965 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
20967 QCPFinancialDataMap map;
20968 int count = qMin(time.size(), value.size());
20970 return QCPFinancialDataMap();
20972 QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
20973 int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
20974 for (int i=0; i<count; ++i)
20976 int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
20977 if (currentBinIndex == index) // data point still in current bin, extend high/low:
20979 if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
20980 if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
20981 if (i == count-1) // last data point is in current bin, finalize bin:
20983 currentBinData.close = value.at(i);
20984 currentBinData.key = timeBinOffset+(index)*timeBinSize;
20985 map.insert(currentBinData.key, currentBinData);
20987 } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
20989 // finalize current bin:
20990 currentBinData.close = value.at(i-1);
20991 currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
20992 map.insert(currentBinData.key, currentBinData);
20994 currentBinIndex = index;
20995 currentBinData.open = value.at(i);
20996 currentBinData.high = value.at(i);
20997 currentBinData.low = value.at(i);
21004 /* inherits documentation from base class */
21005 void QCPFinancial::draw(QCPPainter *painter)
21007 // get visible data range:
21008 QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
21009 getVisibleDataBounds(lower, upper);
21010 if (lower == mData->constEnd() || upper == mData->constEnd())
21013 // draw visible data range according to configured style:
21014 switch (mChartStyle)
21016 case QCPFinancial::csOhlc:
21017 drawOhlcPlot(painter, lower, upper+1); break;
21018 case QCPFinancial::csCandlestick:
21019 drawCandlestickPlot(painter, lower, upper+1); break;
21023 /* inherits documentation from base class */
21024 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
21026 painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
21027 if (mChartStyle == csOhlc)
21031 // draw upper left half icon with positive color:
21032 painter->setBrush(mBrushPositive);
21033 painter->setPen(mPenPositive);
21034 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21035 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21036 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21037 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21038 // draw bottom right hald icon with negative color:
21039 painter->setBrush(mBrushNegative);
21040 painter->setPen(mPenNegative);
21041 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21042 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21043 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21044 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21047 painter->setBrush(mBrush);
21048 painter->setPen(mPen);
21049 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21050 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21051 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21053 } else if (mChartStyle == csCandlestick)
21057 // draw upper left half icon with positive color:
21058 painter->setBrush(mBrushPositive);
21059 painter->setPen(mPenPositive);
21060 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21061 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21062 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21063 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21064 // draw bottom right hald icon with negative color:
21065 painter->setBrush(mBrushNegative);
21066 painter->setPen(mPenNegative);
21067 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21068 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21069 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21070 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21073 painter->setBrush(mBrush);
21074 painter->setPen(mPen);
21075 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21076 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21077 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21082 /* inherits documentation from base class */
21083 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21086 bool haveLower = false;
21087 bool haveUpper = false;
21090 QCPFinancialDataMap::const_iterator it = mData->constBegin();
21091 while (it != mData->constEnd())
21093 current = it.value().key;
21094 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21096 if (current < range.lower || !haveLower)
21098 range.lower = current;
21101 if (current > range.upper || !haveUpper)
21103 range.upper = current;
21109 // determine exact range by including width of bars/flags:
21110 if (haveLower && mKeyAxis)
21111 range.lower = range.lower-mWidth*0.5;
21112 if (haveUpper && mKeyAxis)
21113 range.upper = range.upper+mWidth*0.5;
21114 foundRange = haveLower && haveUpper;
21118 /* inherits documentation from base class */
21119 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21122 bool haveLower = false;
21123 bool haveUpper = false;
21125 QCPFinancialDataMap::const_iterator it = mData->constBegin();
21126 while (it != mData->constEnd())
21129 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21131 if (it.value().high < range.lower || !haveLower)
21133 range.lower = it.value().high;
21136 if (it.value().high > range.upper || !haveUpper)
21138 range.upper = it.value().high;
21143 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21145 if (it.value().low < range.lower || !haveLower)
21147 range.lower = it.value().low;
21150 if (it.value().low > range.upper || !haveUpper)
21152 range.upper = it.value().low;
21159 foundRange = haveLower && haveUpper;
21165 Draws the data from \a begin to \a end as OHLC bars with the provided \a painter.
21167 This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc.
21169 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21171 QCPAxis *keyAxis = mKeyAxis.data();
21172 QCPAxis *valueAxis = mValueAxis.data();
21173 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21177 if (keyAxis->orientation() == Qt::Horizontal)
21179 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21182 linePen = mSelectedPen;
21183 else if (mTwoColored)
21184 linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21187 painter->setPen(linePen);
21188 double keyPixel = keyAxis->coordToPixel(it.value().key);
21189 double openPixel = valueAxis->coordToPixel(it.value().open);
21190 double closePixel = valueAxis->coordToPixel(it.value().close);
21192 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21194 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21195 painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21197 painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21201 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21204 linePen = mSelectedPen;
21205 else if (mTwoColored)
21206 linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21209 painter->setPen(linePen);
21210 double keyPixel = keyAxis->coordToPixel(it.value().key);
21211 double openPixel = valueAxis->coordToPixel(it.value().open);
21212 double closePixel = valueAxis->coordToPixel(it.value().close);
21214 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21216 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21217 painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21219 painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21226 Draws the data from \a begin to \a end as Candlesticks with the provided \a painter.
21228 This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick.
21230 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21232 QCPAxis *keyAxis = mKeyAxis.data();
21233 QCPAxis *valueAxis = mValueAxis.data();
21234 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21239 if (keyAxis->orientation() == Qt::Horizontal)
21241 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21245 linePen = mSelectedPen;
21246 boxBrush = mSelectedBrush;
21247 } else if (mTwoColored)
21249 if (it.value().close >= it.value().open)
21251 linePen = mPenPositive;
21252 boxBrush = mBrushPositive;
21255 linePen = mPenNegative;
21256 boxBrush = mBrushNegative;
21263 painter->setPen(linePen);
21264 painter->setBrush(boxBrush);
21265 double keyPixel = keyAxis->coordToPixel(it.value().key);
21266 double openPixel = valueAxis->coordToPixel(it.value().open);
21267 double closePixel = valueAxis->coordToPixel(it.value().close);
21269 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21271 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21272 // draw open-close box:
21273 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21274 painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21276 } else // keyAxis->orientation() == Qt::Vertical
21278 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21282 linePen = mSelectedPen;
21283 boxBrush = mSelectedBrush;
21284 } else if (mTwoColored)
21286 if (it.value().close >= it.value().open)
21288 linePen = mPenPositive;
21289 boxBrush = mBrushPositive;
21292 linePen = mPenNegative;
21293 boxBrush = mBrushNegative;
21300 painter->setPen(linePen);
21301 painter->setBrush(boxBrush);
21302 double keyPixel = keyAxis->coordToPixel(it.value().key);
21303 double openPixel = valueAxis->coordToPixel(it.value().open);
21304 double closePixel = valueAxis->coordToPixel(it.value().close);
21306 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21308 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21309 // draw open-close box:
21310 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21311 painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21318 This method is a helper function for \ref selectTest. It is used to test for selection when the
21319 chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end.
21321 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21323 QCPAxis *keyAxis = mKeyAxis.data();
21324 QCPAxis *valueAxis = mValueAxis.data();
21325 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21327 double minDistSqr = std::numeric_limits<double>::max();
21328 QCPFinancialDataMap::const_iterator it;
21329 if (keyAxis->orientation() == Qt::Horizontal)
21331 for (it = begin; it != end; ++it)
21333 double keyPixel = keyAxis->coordToPixel(it.value().key);
21334 // calculate distance to backbone:
21335 double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21336 if (currentDistSqr < minDistSqr)
21337 minDistSqr = currentDistSqr;
21339 } else // keyAxis->orientation() == Qt::Vertical
21341 for (it = begin; it != end; ++it)
21343 double keyPixel = keyAxis->coordToPixel(it.value().key);
21344 // calculate distance to backbone:
21345 double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21346 if (currentDistSqr < minDistSqr)
21347 minDistSqr = currentDistSqr;
21350 return qSqrt(minDistSqr);
21355 This method is a helper function for \ref selectTest. It is used to test for selection when the
21356 chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a
21359 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21361 QCPAxis *keyAxis = mKeyAxis.data();
21362 QCPAxis *valueAxis = mValueAxis.data();
21363 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21365 double minDistSqr = std::numeric_limits<double>::max();
21366 QCPFinancialDataMap::const_iterator it;
21367 if (keyAxis->orientation() == Qt::Horizontal)
21369 for (it = begin; it != end; ++it)
21371 double currentDistSqr;
21372 // determine whether pos is in open-close-box:
21373 QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21374 QCPRange boxValueRange(it.value().close, it.value().open);
21375 double posKey, posValue;
21376 pixelsToCoords(pos, posKey, posValue);
21377 if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21379 currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21382 // calculate distance to high/low lines:
21383 double keyPixel = keyAxis->coordToPixel(it.value().key);
21384 double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21385 double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21386 currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21388 if (currentDistSqr < minDistSqr)
21389 minDistSqr = currentDistSqr;
21391 } else // keyAxis->orientation() == Qt::Vertical
21393 for (it = begin; it != end; ++it)
21395 double currentDistSqr;
21396 // determine whether pos is in open-close-box:
21397 QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21398 QCPRange boxValueRange(it.value().close, it.value().open);
21399 double posKey, posValue;
21400 pixelsToCoords(pos, posKey, posValue);
21401 if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21403 currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21406 // calculate distance to high/low lines:
21407 double keyPixel = keyAxis->coordToPixel(it.value().key);
21408 double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21409 double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21410 currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21412 if (currentDistSqr < minDistSqr)
21413 minDistSqr = currentDistSqr;
21416 return qSqrt(minDistSqr);
21421 called by the drawing methods to determine which data (key) range is visible at the current key
21422 axis range setting, so only that needs to be processed.
21424 \a lower returns an iterator to the lowest data point that needs to be taken into account when
21425 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
21426 lower may still be just outside the visible range.
21428 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
21429 just outside of the visible range.
21431 if the plottable contains no data, both \a lower and \a upper point to constEnd.
21433 \see QCPGraph::getVisibleDataBounds
21435 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21437 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21438 if (mData->isEmpty())
21440 lower = mData->constEnd();
21441 upper = mData->constEnd();
21445 // get visible data range as QMap iterators
21446 QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
21447 QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
21448 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21449 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21451 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21452 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21456 ////////////////////////////////////////////////////////////////////////////////////////////////////
21457 //////////////////// QCPItemStraightLine
21458 ////////////////////////////////////////////////////////////////////////////////////////////////////
21460 /*! \class QCPItemStraightLine
21461 \brief A straight line that spans infinitely in both directions
21463 \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
21465 It has two positions, \a point1 and \a point2, which define the straight line.
21469 Creates a straight line item and sets default values.
21471 The constructed item can be added to the plot with QCustomPlot::addItem.
21473 QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
21474 QCPAbstractItem(parentPlot),
21475 point1(createPosition(QLatin1String("point1"))),
21476 point2(createPosition(QLatin1String("point2")))
21478 point1->setCoords(0, 0);
21479 point2->setCoords(1, 1);
21481 setPen(QPen(Qt::black));
21482 setSelectedPen(QPen(Qt::blue,2));
21485 QCPItemStraightLine::~QCPItemStraightLine()
21490 Sets the pen that will be used to draw the line
21492 \see setSelectedPen
21494 void QCPItemStraightLine::setPen(const QPen &pen)
21500 Sets the pen that will be used to draw the line when selected
21502 \see setPen, setSelected
21504 void QCPItemStraightLine::setSelectedPen(const QPen &pen)
21506 mSelectedPen = pen;
21509 /* inherits documentation from base class */
21510 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21513 if (onlySelectable && !mSelectable)
21516 return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21519 /* inherits documentation from base class */
21520 void QCPItemStraightLine::draw(QCPPainter *painter)
21522 QVector2D start(point1->pixelPoint());
21523 QVector2D end(point2->pixelPoint());
21524 // get visible segment of straight line inside clipRect:
21525 double clipPad = mainPen().widthF();
21526 QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21527 // paint visible segment, if existent:
21528 if (!line.isNull())
21530 painter->setPen(mainPen());
21531 painter->drawLine(line);
21537 finds the shortest distance of \a point to the straight line defined by the base point \a
21538 base and the direction vector \a vec.
21540 This is a helper function for \ref selectTest.
21542 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21544 return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21549 Returns the section of the straight line defined by \a base and direction vector \a
21550 vec, that is visible in the specified \a rect.
21552 This is a helper function for \ref draw.
21554 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21559 if (vec.x() == 0 && vec.y() == 0)
21561 if (qFuzzyIsNull(vec.x())) // line is vertical
21563 // check top of rect:
21566 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21567 if (gamma >= 0 && gamma <= rect.width())
21568 result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21569 } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21571 // check left of rect:
21574 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21575 if (gamma >= 0 && gamma <= rect.height())
21576 result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21577 } else // line is skewed
21579 QList<QVector2D> pointVectors;
21580 // check top of rect:
21583 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21584 if (gamma >= 0 && gamma <= rect.width())
21585 pointVectors.append(QVector2D(bx+gamma, by));
21586 // check bottom of rect:
21588 by = rect.bottom();
21589 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21590 if (gamma >= 0 && gamma <= rect.width())
21591 pointVectors.append(QVector2D(bx+gamma, by));
21592 // check left of rect:
21595 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21596 if (gamma >= 0 && gamma <= rect.height())
21597 pointVectors.append(QVector2D(bx, by+gamma));
21598 // check right of rect:
21601 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21602 if (gamma >= 0 && gamma <= rect.height())
21603 pointVectors.append(QVector2D(bx, by+gamma));
21605 // evaluate points:
21606 if (pointVectors.size() == 2)
21608 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21609 } else if (pointVectors.size() > 2)
21611 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21612 double distSqrMax = 0;
21613 QVector2D pv1, pv2;
21614 for (int i=0; i<pointVectors.size()-1; ++i)
21616 for (int k=i+1; k<pointVectors.size(); ++k)
21618 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21619 if (distSqr > distSqrMax)
21621 pv1 = pointVectors.at(i);
21622 pv2 = pointVectors.at(k);
21623 distSqrMax = distSqr;
21627 result.setPoints(pv1.toPointF(), pv2.toPointF());
21635 Returns the pen that should be used for drawing lines. Returns mPen when the
21636 item is not selected and mSelectedPen when it is.
21638 QPen QCPItemStraightLine::mainPen() const
21640 return mSelected ? mSelectedPen : mPen;
21644 ////////////////////////////////////////////////////////////////////////////////////////////////////
21645 //////////////////// QCPItemLine
21646 ////////////////////////////////////////////////////////////////////////////////////////////////////
21648 /*! \class QCPItemLine
21649 \brief A line from one point to another
21651 \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
21653 It has two positions, \a start and \a end, which define the end points of the line.
21655 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
21659 Creates a line item and sets default values.
21661 The constructed item can be added to the plot with QCustomPlot::addItem.
21663 QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
21664 QCPAbstractItem(parentPlot),
21665 start(createPosition(QLatin1String("start"))),
21666 end(createPosition(QLatin1String("end")))
21668 start->setCoords(0, 0);
21669 end->setCoords(1, 1);
21671 setPen(QPen(Qt::black));
21672 setSelectedPen(QPen(Qt::blue,2));
21675 QCPItemLine::~QCPItemLine()
21680 Sets the pen that will be used to draw the line
21682 \see setSelectedPen
21684 void QCPItemLine::setPen(const QPen &pen)
21690 Sets the pen that will be used to draw the line when selected
21692 \see setPen, setSelected
21694 void QCPItemLine::setSelectedPen(const QPen &pen)
21696 mSelectedPen = pen;
21700 Sets the line ending style of the head. The head corresponds to the \a end position.
21702 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21703 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21707 void QCPItemLine::setHead(const QCPLineEnding &head)
21713 Sets the line ending style of the tail. The tail corresponds to the \a start position.
21715 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21716 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21720 void QCPItemLine::setTail(const QCPLineEnding &tail)
21725 /* inherits documentation from base class */
21726 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21729 if (onlySelectable && !mSelectable)
21732 return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
21735 /* inherits documentation from base class */
21736 void QCPItemLine::draw(QCPPainter *painter)
21738 QVector2D startVec(start->pixelPoint());
21739 QVector2D endVec(end->pixelPoint());
21740 if (startVec.toPoint() == endVec.toPoint())
21742 // get visible segment of straight line inside clipRect:
21743 double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
21744 clipPad = qMax(clipPad, (double)mainPen().widthF());
21745 QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21746 // paint visible segment, if existent:
21747 if (!line.isNull())
21749 painter->setPen(mainPen());
21750 painter->drawLine(line);
21751 painter->setBrush(Qt::SolidPattern);
21752 if (mTail.style() != QCPLineEnding::esNone)
21753 mTail.draw(painter, startVec, startVec-endVec);
21754 if (mHead.style() != QCPLineEnding::esNone)
21755 mHead.draw(painter, endVec, endVec-startVec);
21761 Returns the section of the line defined by \a start and \a end, that is visible in the specified
21764 This is a helper function for \ref draw.
21766 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
21768 bool containsStart = rect.contains(start.x(), start.y());
21769 bool containsEnd = rect.contains(end.x(), end.y());
21770 if (containsStart && containsEnd)
21771 return QLineF(start.toPointF(), end.toPointF());
21773 QVector2D base = start;
21774 QVector2D vec = end-start;
21778 QList<QVector2D> pointVectors;
21780 if (!qFuzzyIsNull(vec.y())) // line is not horizontal
21782 // check top of rect:
21785 mu = (by-base.y())/vec.y();
21786 if (mu >= 0 && mu <= 1)
21788 gamma = base.x()-bx + mu*vec.x();
21789 if (gamma >= 0 && gamma <= rect.width())
21790 pointVectors.append(QVector2D(bx+gamma, by));
21792 // check bottom of rect:
21794 by = rect.bottom();
21795 mu = (by-base.y())/vec.y();
21796 if (mu >= 0 && mu <= 1)
21798 gamma = base.x()-bx + mu*vec.x();
21799 if (gamma >= 0 && gamma <= rect.width())
21800 pointVectors.append(QVector2D(bx+gamma, by));
21803 if (!qFuzzyIsNull(vec.x())) // line is not vertical
21805 // check left of rect:
21808 mu = (bx-base.x())/vec.x();
21809 if (mu >= 0 && mu <= 1)
21811 gamma = base.y()-by + mu*vec.y();
21812 if (gamma >= 0 && gamma <= rect.height())
21813 pointVectors.append(QVector2D(bx, by+gamma));
21815 // check right of rect:
21818 mu = (bx-base.x())/vec.x();
21819 if (mu >= 0 && mu <= 1)
21821 gamma = base.y()-by + mu*vec.y();
21822 if (gamma >= 0 && gamma <= rect.height())
21823 pointVectors.append(QVector2D(bx, by+gamma));
21828 pointVectors.append(start);
21830 pointVectors.append(end);
21832 // evaluate points:
21833 if (pointVectors.size() == 2)
21835 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21836 } else if (pointVectors.size() > 2)
21838 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21839 double distSqrMax = 0;
21840 QVector2D pv1, pv2;
21841 for (int i=0; i<pointVectors.size()-1; ++i)
21843 for (int k=i+1; k<pointVectors.size(); ++k)
21845 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21846 if (distSqr > distSqrMax)
21848 pv1 = pointVectors.at(i);
21849 pv2 = pointVectors.at(k);
21850 distSqrMax = distSqr;
21854 result.setPoints(pv1.toPointF(), pv2.toPointF());
21861 Returns the pen that should be used for drawing lines. Returns mPen when the
21862 item is not selected and mSelectedPen when it is.
21864 QPen QCPItemLine::mainPen() const
21866 return mSelected ? mSelectedPen : mPen;
21870 ////////////////////////////////////////////////////////////////////////////////////////////////////
21871 //////////////////// QCPItemCurve
21872 ////////////////////////////////////////////////////////////////////////////////////////////////////
21874 /*! \class QCPItemCurve
21875 \brief A curved line from one point to another
21877 \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
21879 It has four positions, \a start and \a end, which define the end points of the line, and two
21880 control points which define the direction the line exits from the start and the direction from
21881 which it approaches the end: \a startDir and \a endDir.
21883 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
21886 Often it is desirable for the control points to stay at fixed relative positions to the start/end
21887 point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
21888 and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
21892 Creates a curve item and sets default values.
21894 The constructed item can be added to the plot with QCustomPlot::addItem.
21896 QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
21897 QCPAbstractItem(parentPlot),
21898 start(createPosition(QLatin1String("start"))),
21899 startDir(createPosition(QLatin1String("startDir"))),
21900 endDir(createPosition(QLatin1String("endDir"))),
21901 end(createPosition(QLatin1String("end")))
21903 start->setCoords(0, 0);
21904 startDir->setCoords(0.5, 0);
21905 endDir->setCoords(0, 0.5);
21906 end->setCoords(1, 1);
21908 setPen(QPen(Qt::black));
21909 setSelectedPen(QPen(Qt::blue,2));
21912 QCPItemCurve::~QCPItemCurve()
21917 Sets the pen that will be used to draw the line
21919 \see setSelectedPen
21921 void QCPItemCurve::setPen(const QPen &pen)
21927 Sets the pen that will be used to draw the line when selected
21929 \see setPen, setSelected
21931 void QCPItemCurve::setSelectedPen(const QPen &pen)
21933 mSelectedPen = pen;
21937 Sets the line ending style of the head. The head corresponds to the \a end position.
21939 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21940 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21944 void QCPItemCurve::setHead(const QCPLineEnding &head)
21950 Sets the line ending style of the tail. The tail corresponds to the \a start position.
21952 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21953 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21957 void QCPItemCurve::setTail(const QCPLineEnding &tail)
21962 /* inherits documentation from base class */
21963 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21966 if (onlySelectable && !mSelectable)
21969 QPointF startVec(start->pixelPoint());
21970 QPointF startDirVec(startDir->pixelPoint());
21971 QPointF endDirVec(endDir->pixelPoint());
21972 QPointF endVec(end->pixelPoint());
21974 QPainterPath cubicPath(startVec);
21975 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21977 QPolygonF polygon = cubicPath.toSubpathPolygons().first();
21978 double minDistSqr = std::numeric_limits<double>::max();
21979 for (int i=1; i<polygon.size(); ++i)
21981 double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
21982 if (distSqr < minDistSqr)
21983 minDistSqr = distSqr;
21985 return qSqrt(minDistSqr);
21988 /* inherits documentation from base class */
21989 void QCPItemCurve::draw(QCPPainter *painter)
21991 QPointF startVec(start->pixelPoint());
21992 QPointF startDirVec(startDir->pixelPoint());
21993 QPointF endDirVec(endDir->pixelPoint());
21994 QPointF endVec(end->pixelPoint());
21995 if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
21998 QPainterPath cubicPath(startVec);
21999 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
22001 // paint visible segment, if existent:
22002 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22003 QRect cubicRect = cubicPath.controlPointRect().toRect();
22004 if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
22005 cubicRect.adjust(0, 0, 1, 1);
22006 if (clip.intersects(cubicRect))
22008 painter->setPen(mainPen());
22009 painter->drawPath(cubicPath);
22010 painter->setBrush(Qt::SolidPattern);
22011 if (mTail.style() != QCPLineEnding::esNone)
22012 mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
22013 if (mHead.style() != QCPLineEnding::esNone)
22014 mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
22020 Returns the pen that should be used for drawing lines. Returns mPen when the
22021 item is not selected and mSelectedPen when it is.
22023 QPen QCPItemCurve::mainPen() const
22025 return mSelected ? mSelectedPen : mPen;
22029 ////////////////////////////////////////////////////////////////////////////////////////////////////
22030 //////////////////// QCPItemRect
22031 ////////////////////////////////////////////////////////////////////////////////////////////////////
22033 /*! \class QCPItemRect
22036 \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
22038 It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
22042 Creates a rectangle item and sets default values.
22044 The constructed item can be added to the plot with QCustomPlot::addItem.
22046 QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
22047 QCPAbstractItem(parentPlot),
22048 topLeft(createPosition(QLatin1String("topLeft"))),
22049 bottomRight(createPosition(QLatin1String("bottomRight"))),
22050 top(createAnchor(QLatin1String("top"), aiTop)),
22051 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22052 right(createAnchor(QLatin1String("right"), aiRight)),
22053 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22054 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22055 left(createAnchor(QLatin1String("left"), aiLeft))
22057 topLeft->setCoords(0, 1);
22058 bottomRight->setCoords(1, 0);
22060 setPen(QPen(Qt::black));
22061 setSelectedPen(QPen(Qt::blue,2));
22062 setBrush(Qt::NoBrush);
22063 setSelectedBrush(Qt::NoBrush);
22066 QCPItemRect::~QCPItemRect()
22071 Sets the pen that will be used to draw the line of the rectangle
22073 \see setSelectedPen, setBrush
22075 void QCPItemRect::setPen(const QPen &pen)
22081 Sets the pen that will be used to draw the line of the rectangle when selected
22083 \see setPen, setSelected
22085 void QCPItemRect::setSelectedPen(const QPen &pen)
22087 mSelectedPen = pen;
22091 Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
22094 \see setSelectedBrush, setPen
22096 void QCPItemRect::setBrush(const QBrush &brush)
22102 Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
22103 brush to Qt::NoBrush.
22107 void QCPItemRect::setSelectedBrush(const QBrush &brush)
22109 mSelectedBrush = brush;
22112 /* inherits documentation from base class */
22113 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22116 if (onlySelectable && !mSelectable)
22119 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22120 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22121 return rectSelectTest(rect, pos, filledRect);
22124 /* inherits documentation from base class */
22125 void QCPItemRect::draw(QCPPainter *painter)
22127 QPointF p1 = topLeft->pixelPoint();
22128 QPointF p2 = bottomRight->pixelPoint();
22129 if (p1.toPoint() == p2.toPoint())
22131 QRectF rect = QRectF(p1, p2).normalized();
22132 double clipPad = mainPen().widthF();
22133 QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22134 if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22136 painter->setPen(mainPen());
22137 painter->setBrush(mainBrush());
22138 painter->drawRect(rect);
22142 /* inherits documentation from base class */
22143 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22145 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22148 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22149 case aiTopRight: return rect.topRight();
22150 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22151 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22152 case aiBottomLeft: return rect.bottomLeft();
22153 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22156 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22162 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22163 and mSelectedPen when it is.
22165 QPen QCPItemRect::mainPen() const
22167 return mSelected ? mSelectedPen : mPen;
22172 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22173 is not selected and mSelectedBrush when it is.
22175 QBrush QCPItemRect::mainBrush() const
22177 return mSelected ? mSelectedBrush : mBrush;
22181 ////////////////////////////////////////////////////////////////////////////////////////////////////
22182 //////////////////// QCPItemText
22183 ////////////////////////////////////////////////////////////////////////////////////////////////////
22185 /*! \class QCPItemText
22186 \brief A text label
22188 \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
22190 Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
22191 The latter controls which part of the text rect shall be aligned with \a position.
22193 The text alignment itself (i.e. left, center, right) can be controlled with \ref
22196 The text may be rotated around the \a position point with \ref setRotation.
22200 Creates a text item and sets default values.
22202 The constructed item can be added to the plot with QCustomPlot::addItem.
22204 QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
22205 QCPAbstractItem(parentPlot),
22206 position(createPosition(QLatin1String("position"))),
22207 topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22208 top(createAnchor(QLatin1String("top"), aiTop)),
22209 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22210 right(createAnchor(QLatin1String("right"), aiRight)),
22211 bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22212 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22213 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22214 left(createAnchor(QLatin1String("left"), aiLeft))
22216 position->setCoords(0, 0);
22219 setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22220 setPositionAlignment(Qt::AlignCenter);
22221 setText(QLatin1String("text"));
22224 setSelectedPen(Qt::NoPen);
22225 setBrush(Qt::NoBrush);
22226 setSelectedBrush(Qt::NoBrush);
22227 setColor(Qt::black);
22228 setSelectedColor(Qt::blue);
22231 QCPItemText::~QCPItemText()
22236 Sets the color of the text.
22238 void QCPItemText::setColor(const QColor &color)
22244 Sets the color of the text that will be used when the item is selected.
22246 void QCPItemText::setSelectedColor(const QColor &color)
22248 mSelectedColor = color;
22252 Sets the pen that will be used do draw a rectangular border around the text. To disable the
22253 border, set \a pen to Qt::NoPen.
22255 \see setSelectedPen, setBrush, setPadding
22257 void QCPItemText::setPen(const QPen &pen)
22263 Sets the pen that will be used do draw a rectangular border around the text, when the item is
22264 selected. To disable the border, set \a pen to Qt::NoPen.
22268 void QCPItemText::setSelectedPen(const QPen &pen)
22270 mSelectedPen = pen;
22274 Sets the brush that will be used do fill the background of the text. To disable the
22275 background, set \a brush to Qt::NoBrush.
22277 \see setSelectedBrush, setPen, setPadding
22279 void QCPItemText::setBrush(const QBrush &brush)
22285 Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
22286 background, set \a brush to Qt::NoBrush.
22290 void QCPItemText::setSelectedBrush(const QBrush &brush)
22292 mSelectedBrush = brush;
22296 Sets the font of the text.
22298 \see setSelectedFont, setColor
22300 void QCPItemText::setFont(const QFont &font)
22306 Sets the font of the text that will be used when the item is selected.
22310 void QCPItemText::setSelectedFont(const QFont &font)
22312 mSelectedFont = font;
22316 Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
22317 character, e.g. '\n'.
22319 \see setFont, setColor, setTextAlignment
22321 void QCPItemText::setText(const QString &text)
22327 Sets which point of the text rect shall be aligned with \a position.
22330 \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
22331 that the top of the text rect will be horizontally centered on \a position.
22332 \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
22333 bottom left corner of the text rect.
22335 If you want to control the alignment of (multi-lined) text within the text rect, use \ref
22338 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22340 mPositionAlignment = alignment;
22344 Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
22346 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22348 mTextAlignment = alignment;
22352 Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
22353 around \a position.
22355 void QCPItemText::setRotation(double degrees)
22357 mRotation = degrees;
22361 Sets the distance between the border of the text rectangle and the text. The appearance (and
22362 visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
22364 void QCPItemText::setPadding(const QMargins &padding)
22366 mPadding = padding;
22369 /* inherits documentation from base class */
22370 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22373 if (onlySelectable && !mSelectable)
22376 // The rect may be rotated, so we transform the actual clicked pos to the rotated
22377 // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22378 QPointF positionPixels(position->pixelPoint());
22379 QTransform inputTransform;
22380 inputTransform.translate(positionPixels.x(), positionPixels.y());
22381 inputTransform.rotate(-mRotation);
22382 inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22383 QPointF rotatedPos = inputTransform.map(pos);
22384 QFontMetrics fontMetrics(mFont);
22385 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22386 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22387 QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22388 textBoxRect.moveTopLeft(textPos.toPoint());
22390 return rectSelectTest(textBoxRect, rotatedPos, true);
22393 /* inherits documentation from base class */
22394 void QCPItemText::draw(QCPPainter *painter)
22396 QPointF pos(position->pixelPoint());
22397 QTransform transform = painter->transform();
22398 transform.translate(pos.x(), pos.y());
22399 if (!qFuzzyIsNull(mRotation))
22400 transform.rotate(mRotation);
22401 painter->setFont(mainFont());
22402 QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22403 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22404 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22405 textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22406 textBoxRect.moveTopLeft(textPos.toPoint());
22407 double clipPad = mainPen().widthF();
22408 QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22409 if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22411 painter->setTransform(transform);
22412 if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22413 (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22415 painter->setPen(mainPen());
22416 painter->setBrush(mainBrush());
22417 painter->drawRect(textBoxRect);
22419 painter->setBrush(Qt::NoBrush);
22420 painter->setPen(QPen(mainColor()));
22421 painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22425 /* inherits documentation from base class */
22426 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22428 // get actual rect points (pretty much copied from draw function):
22429 QPointF pos(position->pixelPoint());
22430 QTransform transform;
22431 transform.translate(pos.x(), pos.y());
22432 if (!qFuzzyIsNull(mRotation))
22433 transform.rotate(mRotation);
22434 QFontMetrics fontMetrics(mainFont());
22435 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22436 QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22437 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22438 textBoxRect.moveTopLeft(textPos.toPoint());
22439 QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22443 case aiTopLeft: return rectPoly.at(0);
22444 case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22445 case aiTopRight: return rectPoly.at(1);
22446 case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22447 case aiBottomRight: return rectPoly.at(2);
22448 case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22449 case aiBottomLeft: return rectPoly.at(3);
22450 case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22453 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22459 Returns the point that must be given to the QPainter::drawText function (which expects the top
22460 left point of the text rect), according to the position \a pos, the text bounding box \a rect and
22461 the requested \a positionAlignment.
22463 For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
22464 will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
22465 drawn at that point, the lower left corner of the resulting text rect is at \a pos.
22467 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22469 if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22472 QPointF result = pos; // start at top left
22473 if (positionAlignment.testFlag(Qt::AlignHCenter))
22474 result.rx() -= rect.width()/2.0;
22475 else if (positionAlignment.testFlag(Qt::AlignRight))
22476 result.rx() -= rect.width();
22477 if (positionAlignment.testFlag(Qt::AlignVCenter))
22478 result.ry() -= rect.height()/2.0;
22479 else if (positionAlignment.testFlag(Qt::AlignBottom))
22480 result.ry() -= rect.height();
22486 Returns the font that should be used for drawing text. Returns mFont when the item is not selected
22487 and mSelectedFont when it is.
22489 QFont QCPItemText::mainFont() const
22491 return mSelected ? mSelectedFont : mFont;
22496 Returns the color that should be used for drawing text. Returns mColor when the item is not
22497 selected and mSelectedColor when it is.
22499 QColor QCPItemText::mainColor() const
22501 return mSelected ? mSelectedColor : mColor;
22506 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22507 and mSelectedPen when it is.
22509 QPen QCPItemText::mainPen() const
22511 return mSelected ? mSelectedPen : mPen;
22516 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22517 is not selected and mSelectedBrush when it is.
22519 QBrush QCPItemText::mainBrush() const
22521 return mSelected ? mSelectedBrush : mBrush;
22525 ////////////////////////////////////////////////////////////////////////////////////////////////////
22526 //////////////////// QCPItemEllipse
22527 ////////////////////////////////////////////////////////////////////////////////////////////////////
22529 /*! \class QCPItemEllipse
22532 \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
22534 It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
22538 Creates an ellipse item and sets default values.
22540 The constructed item can be added to the plot with QCustomPlot::addItem.
22542 QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
22543 QCPAbstractItem(parentPlot),
22544 topLeft(createPosition(QLatin1String("topLeft"))),
22545 bottomRight(createPosition(QLatin1String("bottomRight"))),
22546 topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22547 top(createAnchor(QLatin1String("top"), aiTop)),
22548 topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22549 right(createAnchor(QLatin1String("right"), aiRight)),
22550 bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22551 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22552 bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22553 left(createAnchor(QLatin1String("left"), aiLeft)),
22554 center(createAnchor(QLatin1String("center"), aiCenter))
22556 topLeft->setCoords(0, 1);
22557 bottomRight->setCoords(1, 0);
22559 setPen(QPen(Qt::black));
22560 setSelectedPen(QPen(Qt::blue, 2));
22561 setBrush(Qt::NoBrush);
22562 setSelectedBrush(Qt::NoBrush);
22565 QCPItemEllipse::~QCPItemEllipse()
22570 Sets the pen that will be used to draw the line of the ellipse
22572 \see setSelectedPen, setBrush
22574 void QCPItemEllipse::setPen(const QPen &pen)
22580 Sets the pen that will be used to draw the line of the ellipse when selected
22582 \see setPen, setSelected
22584 void QCPItemEllipse::setSelectedPen(const QPen &pen)
22586 mSelectedPen = pen;
22590 Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
22593 \see setSelectedBrush, setPen
22595 void QCPItemEllipse::setBrush(const QBrush &brush)
22601 Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
22602 brush to Qt::NoBrush.
22606 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
22608 mSelectedBrush = brush;
22611 /* inherits documentation from base class */
22612 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22615 if (onlySelectable && !mSelectable)
22618 double result = -1;
22619 QPointF p1 = topLeft->pixelPoint();
22620 QPointF p2 = bottomRight->pixelPoint();
22621 QPointF center((p1+p2)/2.0);
22622 double a = qAbs(p1.x()-p2.x())/2.0;
22623 double b = qAbs(p1.y()-p2.y())/2.0;
22624 double x = pos.x()-center.x();
22625 double y = pos.y()-center.y();
22627 // distance to border:
22628 double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
22629 result = qAbs(c-1)*qSqrt(x*x+y*y);
22630 // filled ellipse, allow click inside to count as hit:
22631 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
22633 if (x*x/(a*a) + y*y/(b*b) <= 1)
22634 result = mParentPlot->selectionTolerance()*0.99;
22639 /* inherits documentation from base class */
22640 void QCPItemEllipse::draw(QCPPainter *painter)
22642 QPointF p1 = topLeft->pixelPoint();
22643 QPointF p2 = bottomRight->pixelPoint();
22644 if (p1.toPoint() == p2.toPoint())
22646 QRectF ellipseRect = QRectF(p1, p2).normalized();
22647 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22648 if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
22650 painter->setPen(mainPen());
22651 painter->setBrush(mainBrush());
22652 #ifdef __EXCEPTIONS
22653 try // drawEllipse sometimes throws exceptions if ellipse is too big
22656 painter->drawEllipse(ellipseRect);
22657 #ifdef __EXCEPTIONS
22660 qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
22667 /* inherits documentation from base class */
22668 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
22670 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22673 case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
22674 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22675 case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
22676 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22677 case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
22678 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22679 case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
22680 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22681 case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
22684 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22690 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22691 and mSelectedPen when it is.
22693 QPen QCPItemEllipse::mainPen() const
22695 return mSelected ? mSelectedPen : mPen;
22700 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22701 is not selected and mSelectedBrush when it is.
22703 QBrush QCPItemEllipse::mainBrush() const
22705 return mSelected ? mSelectedBrush : mBrush;
22709 ////////////////////////////////////////////////////////////////////////////////////////////////////
22710 //////////////////// QCPItemPixmap
22711 ////////////////////////////////////////////////////////////////////////////////////////////////////
22713 /*! \class QCPItemPixmap
22714 \brief An arbitrary pixmap
22716 \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
22718 It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
22719 be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
22720 fit the rectangle or be drawn aligned to the topLeft position.
22722 If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
22723 on the right side of the example image), the pixmap will be flipped in the respective
22728 Creates a rectangle item and sets default values.
22730 The constructed item can be added to the plot with QCustomPlot::addItem.
22732 QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
22733 QCPAbstractItem(parentPlot),
22734 topLeft(createPosition(QLatin1String("topLeft"))),
22735 bottomRight(createPosition(QLatin1String("bottomRight"))),
22736 top(createAnchor(QLatin1String("top"), aiTop)),
22737 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22738 right(createAnchor(QLatin1String("right"), aiRight)),
22739 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22740 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22741 left(createAnchor(QLatin1String("left"), aiLeft)),
22742 mScaledPixmapInvalidated(true)
22744 topLeft->setCoords(0, 1);
22745 bottomRight->setCoords(1, 0);
22748 setSelectedPen(QPen(Qt::blue));
22749 setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
22752 QCPItemPixmap::~QCPItemPixmap()
22757 Sets the pixmap that will be displayed.
22759 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
22762 mScaledPixmapInvalidated = true;
22763 if (mPixmap.isNull())
22764 qDebug() << Q_FUNC_INFO << "pixmap is null";
22768 Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
22769 bottomRight positions.
22771 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
22774 mAspectRatioMode = aspectRatioMode;
22775 mTransformationMode = transformationMode;
22776 mScaledPixmapInvalidated = true;
22780 Sets the pen that will be used to draw a border around the pixmap.
22782 \see setSelectedPen, setBrush
22784 void QCPItemPixmap::setPen(const QPen &pen)
22790 Sets the pen that will be used to draw a border around the pixmap when selected
22792 \see setPen, setSelected
22794 void QCPItemPixmap::setSelectedPen(const QPen &pen)
22796 mSelectedPen = pen;
22799 /* inherits documentation from base class */
22800 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22803 if (onlySelectable && !mSelectable)
22806 return rectSelectTest(getFinalRect(), pos, true);
22809 /* inherits documentation from base class */
22810 void QCPItemPixmap::draw(QCPPainter *painter)
22812 bool flipHorz = false;
22813 bool flipVert = false;
22814 QRect rect = getFinalRect(&flipHorz, &flipVert);
22815 double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
22816 QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22817 if (boundingRect.intersects(clipRect()))
22819 updateScaledPixmap(rect, flipHorz, flipVert);
22820 painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
22821 QPen pen = mainPen();
22822 if (pen.style() != Qt::NoPen)
22824 painter->setPen(pen);
22825 painter->setBrush(Qt::NoBrush);
22826 painter->drawRect(rect);
22831 /* inherits documentation from base class */
22832 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
22836 QRect rect = getFinalRect(&flipHorz, &flipVert);
22837 // we actually want denormal rects (negative width/height) here, so restore
22838 // the flipped state:
22840 rect.adjust(rect.width(), 0, -rect.width(), 0);
22842 rect.adjust(0, rect.height(), 0, -rect.height());
22846 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22847 case aiTopRight: return rect.topRight();
22848 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22849 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22850 case aiBottomLeft: return rect.bottomLeft();
22851 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
22854 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22860 Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
22861 parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
22862 horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
22865 This function only creates the scaled pixmap when the buffered pixmap has a different size than
22866 the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
22867 not cause expensive rescaling every time.
22869 If scaling is disabled, sets mScaledPixmap to a null QPixmap.
22871 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
22873 if (mPixmap.isNull())
22878 if (finalRect.isNull())
22879 finalRect = getFinalRect(&flipHorz, &flipVert);
22880 if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size())
22882 mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
22883 if (flipHorz || flipVert)
22884 mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
22886 } else if (!mScaledPixmap.isNull())
22887 mScaledPixmap = QPixmap();
22888 mScaledPixmapInvalidated = false;
22893 Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
22894 and scaling settings.
22896 The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
22897 flipped horizontally or vertically in the returned rect. (The returned rect itself is always
22898 normalized, i.e. the top left corner of the rect is actually further to the top/left than the
22899 bottom right corner). This is the case when the item position \a topLeft is further to the
22900 bottom/right than \a bottomRight.
22902 If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
22903 aligned with the item position \a topLeft. The position \a bottomRight is ignored.
22905 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
22908 bool flipHorz = false;
22909 bool flipVert = false;
22910 QPoint p1 = topLeft->pixelPoint().toPoint();
22911 QPoint p2 = bottomRight->pixelPoint().toPoint();
22913 return QRect(p1, QSize(0, 0));
22916 QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
22917 QPoint topLeft = p1;
22918 if (newSize.width() < 0)
22921 newSize.rwidth() *= -1;
22922 topLeft.setX(p2.x());
22924 if (newSize.height() < 0)
22927 newSize.rheight() *= -1;
22928 topLeft.setY(p2.y());
22930 QSize scaledSize = mPixmap.size();
22931 scaledSize.scale(newSize, mAspectRatioMode);
22932 result = QRect(topLeft, scaledSize);
22935 result = QRect(p1, mPixmap.size());
22938 *flippedHorz = flipHorz;
22940 *flippedVert = flipVert;
22946 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22947 and mSelectedPen when it is.
22949 QPen QCPItemPixmap::mainPen() const
22951 return mSelected ? mSelectedPen : mPen;
22955 ////////////////////////////////////////////////////////////////////////////////////////////////////
22956 //////////////////// QCPItemTracer
22957 ////////////////////////////////////////////////////////////////////////////////////////////////////
22959 /*! \class QCPItemTracer
22960 \brief Item that sticks to QCPGraph data points
22962 \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
22964 The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
22965 the coordinate axes of the graph and update its \a position to be on the graph's data. This means
22966 the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
22967 QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a
22968 position will have no effect because they will be overriden in the next redraw (this is when the
22969 coordinate update happens).
22971 If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
22972 stay at the corresponding end of the graph.
22974 With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
22975 points or whether it interpolates data points linearly, if given a key that lies between two data
22976 points of the graph.
22978 The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
22979 have no own visual appearance (set the style to \ref tsNone), and just connect other item
22980 positions to the tracer \a position (used as an anchor) via \ref
22981 QCPItemPosition::setParentAnchor.
22983 \note The tracer position is only automatically updated upon redraws. So when the data of the
22984 graph changes and immediately afterwards (without a redraw) the a position coordinates of the
22985 tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref
22986 updatePosition must be called manually, prior to reading the tracer coordinates.
22990 Creates a tracer item and sets default values.
22992 The constructed item can be added to the plot with QCustomPlot::addItem.
22994 QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
22995 QCPAbstractItem(parentPlot),
22996 position(createPosition(QLatin1String("position"))),
22999 position->setCoords(0, 0);
23001 setBrush(Qt::NoBrush);
23002 setSelectedBrush(Qt::NoBrush);
23003 setPen(QPen(Qt::black));
23004 setSelectedPen(QPen(Qt::blue, 2));
23005 setStyle(tsCrosshair);
23007 setInterpolating(false);
23011 QCPItemTracer::~QCPItemTracer()
23016 Sets the pen that will be used to draw the line of the tracer
23018 \see setSelectedPen, setBrush
23020 void QCPItemTracer::setPen(const QPen &pen)
23026 Sets the pen that will be used to draw the line of the tracer when selected
23028 \see setPen, setSelected
23030 void QCPItemTracer::setSelectedPen(const QPen &pen)
23032 mSelectedPen = pen;
23036 Sets the brush that will be used to draw any fills of the tracer
23038 \see setSelectedBrush, setPen
23040 void QCPItemTracer::setBrush(const QBrush &brush)
23046 Sets the brush that will be used to draw any fills of the tracer, when selected.
23048 \see setBrush, setSelected
23050 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
23052 mSelectedBrush = brush;
23056 Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
23057 does, \ref tsCrosshair does not).
23059 void QCPItemTracer::setSize(double size)
23065 Sets the style/visual appearance of the tracer.
23067 If you only want to use the tracer \a position as an anchor for other items, set \a style to
23070 void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
23076 Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
23077 QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
23079 To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
23080 freely like any other item position. This is the state the tracer will assume when its graph gets
23081 deleted while still attached to it.
23085 void QCPItemTracer::setGraph(QCPGraph *graph)
23089 if (graph->parentPlot() == mParentPlot)
23091 position->setType(QCPItemPosition::ptPlotCoords);
23092 position->setAxes(graph->keyAxis(), graph->valueAxis());
23096 qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23104 Sets the key of the graph's data point the tracer will be positioned at. This is the only free
23105 coordinate of a tracer when attached to a graph.
23107 Depending on \ref setInterpolating, the tracer will be either positioned on the data point
23108 closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
23110 \see setGraph, setInterpolating
23112 void QCPItemTracer::setGraphKey(double key)
23118 Sets whether the value of the graph's data points shall be interpolated, when positioning the
23121 If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
23122 the data point of the graph which is closest to the key, but which is not necessarily exactly
23123 there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
23124 the appropriate value will be interpolated from the graph's data points linearly.
23126 \see setGraph, setGraphKey
23128 void QCPItemTracer::setInterpolating(bool enabled)
23130 mInterpolating = enabled;
23133 /* inherits documentation from base class */
23134 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23137 if (onlySelectable && !mSelectable)
23140 QPointF center(position->pixelPoint());
23141 double w = mSize/2.0;
23142 QRect clip = clipRect();
23145 case tsNone: return -1;
23148 if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23149 return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23150 distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23155 return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23156 distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23160 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23162 // distance to border:
23163 double centerDist = QVector2D(center-pos).length();
23164 double circleLine = w;
23165 double result = qAbs(centerDist-circleLine);
23166 // filled ellipse, allow click inside to count as hit:
23167 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23169 if (centerDist <= circleLine)
23170 result = mParentPlot->selectionTolerance()*0.99;
23178 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23180 QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23181 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23182 return rectSelectTest(rect, pos, filledRect);
23190 /* inherits documentation from base class */
23191 void QCPItemTracer::draw(QCPPainter *painter)
23194 if (mStyle == tsNone)
23197 painter->setPen(mainPen());
23198 painter->setBrush(mainBrush());
23199 QPointF center(position->pixelPoint());
23200 double w = mSize/2.0;
23201 QRect clip = clipRect();
23204 case tsNone: return;
23207 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23209 painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23210 painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23216 if (center.y() > clip.top() && center.y() < clip.bottom())
23217 painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23218 if (center.x() > clip.left() && center.x() < clip.right())
23219 painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23224 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23225 painter->drawEllipse(center, w, w);
23230 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23231 painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23238 If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
23239 position to reside on the graph data, depending on the configured key (\ref setGraphKey).
23241 It is called automatically on every redraw and normally doesn't need to be called manually. One
23242 exception is when you want to read the tracer coordinates via \a position and are not sure that
23243 the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
23244 In that situation, call this function before accessing \a position, to make sure you don't get
23245 out-of-date coordinates.
23247 If there is no graph set on this tracer, this function does nothing.
23249 void QCPItemTracer::updatePosition()
23253 if (mParentPlot->hasPlottable(mGraph))
23255 if (mGraph->data()->size() > 1)
23257 QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23258 QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
23259 if (mGraphKey < first.key())
23260 position->setCoords(first.key(), first.value().value);
23261 else if (mGraphKey > last.key())
23262 position->setCoords(last.key(), last.value().value);
23265 QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23266 if (it != first) // mGraphKey is somewhere between iterators
23268 QCPDataMap::const_iterator prevIt = it-1;
23269 if (mInterpolating)
23271 // interpolate between iterators around mGraphKey:
23273 if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23274 slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23275 position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23278 // find iterator with key closest to mGraphKey:
23279 if (mGraphKey < (prevIt.key()+it.key())*0.5)
23281 position->setCoords(it.key(), it.value().value);
23283 } else // mGraphKey is exactly on first iterator
23284 position->setCoords(it.key(), it.value().value);
23286 } else if (mGraph->data()->size() == 1)
23288 QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23289 position->setCoords(it.key(), it.value().value);
23291 qDebug() << Q_FUNC_INFO << "graph has no data";
23293 qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23299 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
23300 and mSelectedPen when it is.
23302 QPen QCPItemTracer::mainPen() const
23304 return mSelected ? mSelectedPen : mPen;
23309 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
23310 is not selected and mSelectedBrush when it is.
23312 QBrush QCPItemTracer::mainBrush() const
23314 return mSelected ? mSelectedBrush : mBrush;
23318 ////////////////////////////////////////////////////////////////////////////////////////////////////
23319 //////////////////// QCPItemBracket
23320 ////////////////////////////////////////////////////////////////////////////////////////////////////
23322 /*! \class QCPItemBracket
23323 \brief A bracket for referencing/highlighting certain parts in the plot.
23325 \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
23327 It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
23328 actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
23331 The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
23332 stretches away from the embraced span, can be controlled with \ref setLength.
23334 \image html QCPItemBracket-length.png
23335 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23336 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23338 It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
23339 or QCPItemCurve) or a text label (QCPItemText), to the bracket.
23343 Creates a bracket item and sets default values.
23345 The constructed item can be added to the plot with QCustomPlot::addItem.
23347 QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
23348 QCPAbstractItem(parentPlot),
23349 left(createPosition(QLatin1String("left"))),
23350 right(createPosition(QLatin1String("right"))),
23351 center(createAnchor(QLatin1String("center"), aiCenter))
23353 left->setCoords(0, 0);
23354 right->setCoords(1, 1);
23356 setPen(QPen(Qt::black));
23357 setSelectedPen(QPen(Qt::blue, 2));
23359 setStyle(bsCalligraphic);
23362 QCPItemBracket::~QCPItemBracket()
23367 Sets the pen that will be used to draw the bracket.
23369 Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
23370 stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
23371 \ref setLength, which has a similar effect.
23373 \see setSelectedPen
23375 void QCPItemBracket::setPen(const QPen &pen)
23381 Sets the pen that will be used to draw the bracket when selected
23383 \see setPen, setSelected
23385 void QCPItemBracket::setSelectedPen(const QPen &pen)
23387 mSelectedPen = pen;
23391 Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
23392 span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
23394 \image html QCPItemBracket-length.png
23395 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23396 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23398 void QCPItemBracket::setLength(double length)
23404 Sets the style of the bracket, i.e. the shape/visual appearance.
23408 void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
23413 /* inherits documentation from base class */
23414 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23417 if (onlySelectable && !mSelectable)
23420 QVector2D leftVec(left->pixelPoint());
23421 QVector2D rightVec(right->pixelPoint());
23422 if (leftVec.toPoint() == rightVec.toPoint())
23425 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23426 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23427 lengthVec = lengthVec.normalized()*mLength;
23428 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23432 case QCPItemBracket::bsSquare:
23433 case QCPItemBracket::bsRound:
23435 double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23436 double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos);
23437 double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23438 return qSqrt(qMin(qMin(a, b), c));
23440 case QCPItemBracket::bsCurly:
23441 case QCPItemBracket::bsCalligraphic:
23443 double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23444 double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23445 double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23446 double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23447 return qSqrt(qMin(qMin(a, b), qMin(c, d)));
23453 /* inherits documentation from base class */
23454 void QCPItemBracket::draw(QCPPainter *painter)
23456 QVector2D leftVec(left->pixelPoint());
23457 QVector2D rightVec(right->pixelPoint());
23458 if (leftVec.toPoint() == rightVec.toPoint())
23461 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23462 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23463 lengthVec = lengthVec.normalized()*mLength;
23464 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23466 QPolygon boundingPoly;
23467 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23468 << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23469 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23470 if (clip.intersects(boundingPoly.boundingRect()))
23472 painter->setPen(mainPen());
23477 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23478 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23479 painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23484 painter->setBrush(Qt::NoBrush);
23486 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23487 path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23488 path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23489 painter->drawPath(path);
23494 painter->setBrush(Qt::NoBrush);
23496 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23497 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23498 path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23499 painter->drawPath(path);
23502 case bsCalligraphic:
23504 painter->setPen(Qt::NoPen);
23505 painter->setBrush(QBrush(mainPen().color()));
23507 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23509 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23510 path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23512 path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23513 path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23515 painter->drawPath(path);
23522 /* inherits documentation from base class */
23523 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23525 QVector2D leftVec(left->pixelPoint());
23526 QVector2D rightVec(right->pixelPoint());
23527 if (leftVec.toPoint() == rightVec.toPoint())
23528 return leftVec.toPointF();
23530 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23531 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23532 lengthVec = lengthVec.normalized()*mLength;
23533 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23538 return centerVec.toPointF();
23540 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23546 Returns the pen that should be used for drawing lines. Returns mPen when the
23547 item is not selected and mSelectedPen when it is.
23549 QPen QCPItemBracket::mainPen() const
23551 return mSelected ? mSelectedPen : mPen;