removed PrefixPath debug line
[opentx.git] / companion / src / qcustomplot / qcustomplot.cpp
blobd7e54852614e96126bc221b632c9a00b7e90152e
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011-2015 Emanuel Eichhammer **
5 ** **
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. **
10 ** **
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. **
15 ** **
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/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 25.04.15 **
23 ** Version: 1.3.1 **
24 ****************************************************************************/
26 #include "qcustomplot.h"
30 ////////////////////////////////////////////////////////////////////////////////////////////////////
31 //////////////////// QCPPainter
32 ////////////////////////////////////////////////////////////////////////////////////////////////////
34 /*! \class QCPPainter
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
39 for QPainter quirks.
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).
47 /*!
48 Creates a new QCPPainter instance and sets default values
50 QCPPainter::QCPPainter() :
51 QPainter(),
52 mModes(pmDefault),
53 mIsAntialiasing(false)
55 // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
56 // a call to begin() will follow
59 /*!
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) :
66 QPainter(device),
67 mModes(pmDefault),
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.
71 if (isActive())
72 setRenderHint(QPainter::NonCosmeticDefaultPen);
73 #endif
76 QCPPainter::~QCPPainter()
80 /*!
81 Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
82 QCPPainter.
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))
90 makeNonCosmetic();
93 /*! \overload
95 Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
96 this QCPPainter.
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))
104 makeNonCosmetic();
107 /*! \overload
109 Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
110 this QCPPainter.
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))
118 makeNonCosmetic();
121 /*! \overload
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);
133 else
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
141 AA/Non-AA painting).
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
151 if (mIsAntialiasing)
152 translate(0.5, 0.5);
153 else
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)
165 mModes = 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
172 behaviour.
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.
183 if (result)
184 setRenderHint(QPainter::NonCosmeticDefaultPen);
185 #endif
186 return result;
189 /*! \overload
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))
197 mModes &= ~mode;
198 else if (enabled && !mModes.testFlag(mode))
199 mModes |= 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.
208 \see restore
210 void QCPPainter::save()
212 mAntialiasingStack.push(mIsAntialiasing);
213 QPainter::save();
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.
222 \see save
224 void QCPPainter::restore()
226 if (!mAntialiasingStack.isEmpty())
227 mIsAntialiasing = mAntialiasingStack.pop();
228 else
229 qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
230 QPainter::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()))
241 QPen p = pen();
242 p.setWidth(1);
243 QPainter::setPen(p);
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.
308 \see setShape
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
318 style.
320 \see setPen
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() :
332 mSize(6),
333 mShape(ssNone),
334 mPen(Qt::NoPen),
335 mBrush(Qt::NoBrush),
336 mPenDefined(false)
341 Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or
342 brush is defined.
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) :
348 mSize(size),
349 mShape(shape),
350 mPen(Qt::NoPen),
351 mBrush(Qt::NoBrush),
352 mPenDefined(false)
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) :
361 mSize(size),
362 mShape(shape),
363 mPen(QPen(color)),
364 mBrush(Qt::NoBrush),
365 mPenDefined(true)
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) :
374 mSize(size),
375 mShape(shape),
376 mPen(QPen(color)),
377 mBrush(QBrush(fill)),
378 mPenDefined(true)
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
395 wanted.
397 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
398 mSize(size),
399 mShape(shape),
400 mPen(pen),
401 mBrush(brush),
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) :
411 mSize(5),
412 mShape(ssPixmap),
413 mPen(Qt::NoPen),
414 mBrush(Qt::NoBrush),
415 mPixmap(pixmap),
416 mPenDefined(false)
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) :
430 mSize(size),
431 mShape(ssCustom),
432 mPen(pen),
433 mBrush(brush),
434 mCustomPath(customPath),
435 mPenDefined(pen.style() != Qt::NoPen)
440 Sets the size (pixel diameter) of the drawn scatter points to \a size.
442 \see setShape
444 void QCPScatterStyle::setSize(double size)
446 mSize = 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.
455 \see setSize
457 void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)
459 mShape = 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>.
468 \see setBrush
470 void QCPScatterStyle::setPen(const QPen &pen)
472 mPenDefined = true;
473 mPen = 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.
480 \see setPen
482 void QCPScatterStyle::setBrush(const QBrush &brush)
484 mBrush = 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)
496 setShape(ssPixmap);
497 mPixmap = 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)
507 setShape(ssCustom);
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.
518 \see drawShape
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.
532 \see applyTo
534 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
536 drawShape(painter, pos.x(), pos.y());
539 /*! \overload
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;
545 switch (mShape)
547 case ssNone: break;
548 case ssDot:
550 painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
551 break;
553 case ssCross:
555 painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
556 painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
557 break;
559 case ssPlus:
561 painter->drawLine(QLineF(x-w, y, x+w, y));
562 painter->drawLine(QLineF( x, y+w, x, y-w));
563 break;
565 case ssCircle:
567 painter->drawEllipse(QPointF(x , y), w, w);
568 break;
570 case ssDisc:
572 QBrush b = painter->brush();
573 painter->setBrush(painter->pen().color());
574 painter->drawEllipse(QPointF(x , y), w, w);
575 painter->setBrush(b);
576 break;
578 case ssSquare:
580 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
581 break;
583 case ssDiamond:
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));
589 break;
591 case ssStar:
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));
597 break;
599 case ssTriangle:
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));
604 break;
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));
611 break;
613 case ssCrossSquare:
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));
618 break;
620 case ssPlusSquare:
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));
625 break;
627 case ssCrossCircle:
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);
632 break;
634 case ssPlusCircle:
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);
639 break;
641 case ssPeace:
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);
647 break;
649 case ssPixmap:
651 painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
652 break;
654 case ssCustom:
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);
661 break;
667 ////////////////////////////////////////////////////////////////////////////////////////////////////
668 //////////////////// QCPLayer
669 ////////////////////////////////////////////////////////////////////////////////////////////////////
671 /*! \class QCPLayer
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) :
740 QObject(parentPlot),
741 mParentPlot(parentPlot),
742 mName(layerName),
743 mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
744 mVisible(true)
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)
774 mVisible = visible;
777 /*! \internal
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.)
785 \see removeChild
787 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
789 if (!mChildren.contains(layerable))
791 if (prepend)
792 mChildren.prepend(layerable);
793 else
794 mChildren.append(layerable);
795 } else
796 qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
799 /*! \internal
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.)
806 \see addChild
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
823 etc.
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
850 \internal
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
875 line with.
878 /*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
879 \internal
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
895 a different layer.
897 \see setLayer
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
906 derived classes.
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 parents
916 are mainly used to control visibility in a hierarchy of layerables. This means a layerable is
917 only drawn, if all its ancestor layerables are also visible. Note that \a parentLayerable does
918 not become the QObject-parent (for memory management) of this layerable, \a plot does.
920 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
921 QObject(plot),
922 mVisible(true),
923 mParentPlot(plot),
924 mParentLayerable(parentLayerable),
925 mLayer(0),
926 mAntialiased(true)
928 if (mParentPlot)
930 if (targetLayer.isEmpty())
931 setLayer(mParentPlot->currentLayer());
932 else if (!setLayer(targetLayer))
933 qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
937 QCPLayerable::~QCPLayerable()
939 if (mLayer)
941 mLayer->removeChild(this);
942 mLayer = 0;
947 Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
948 on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not
949 possible.
951 void QCPLayerable::setVisible(bool on)
953 mVisible = on;
957 Sets the \a layer of this layerable object. The object will be placed on top of the other objects
958 already on \a layer.
960 Returns true on success, i.e. if \a layer is a valid layer.
962 bool QCPLayerable::setLayer(QCPLayer *layer)
964 return moveToLayer(layer, false);
967 /*! \overload
968 Sets the layer of this layerable object by name
970 Returns true on success, i.e. if \a layerName is a valid layer name.
972 bool QCPLayerable::setLayer(const QString &layerName)
974 if (!mParentPlot)
976 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
977 return false;
979 if (QCPLayer *layer = mParentPlot->layer(layerName))
981 return setLayer(layer);
982 } else
984 qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
985 return false;
990 Sets whether this object will be drawn antialiased or not.
992 Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
993 QCustomPlot::setNotAntialiasedElements.
995 void QCPLayerable::setAntialiased(bool enabled)
997 mAntialiased = enabled;
1001 Returns whether this layerable is visible, taking the visibility of the layerable parent and the
1002 visibility of the layer this layerable is on into account. This is the method that is consulted
1003 to decide whether a layerable shall be drawn or not.
1005 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
1006 subclasses, like in the case of QCPLayoutElement), this function returns true only if this
1007 layerable has its visibility set to true and the parent layerable's \ref realVisibility returns
1008 true.
1010 If this layerable doesn't have a direct layerable parent, returns the state of this layerable's
1011 visibility.
1013 bool QCPLayerable::realVisibility() const
1015 return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1019 This function is used to decide whether a click hits a layerable object or not.
1021 \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
1022 shortest pixel distance of this point to the object. If the object is either invisible or the
1023 distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the
1024 object is not selectable, -1.0 is returned, too.
1026 If the object is represented not by single lines but by an area like a \ref QCPItemText or the
1027 bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In
1028 these cases this function thus returns a constant value greater zero but still below the parent
1029 plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99).
1031 Providing a constant value for area objects allows selecting line objects even when they are
1032 obscured by such area objects, by clicking close to the lines (i.e. closer than
1033 0.99*selectionTolerance).
1035 The actual setting of the selection state is not done by this function. This is handled by the
1036 parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified
1037 via the selectEvent/deselectEvent methods.
1039 \a details is an optional output parameter. Every layerable subclass may place any information
1040 in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot
1041 decides on the basis of this selectTest call, that the object was successfully selected. The
1042 subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part
1043 objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked
1044 is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be
1045 placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
1046 selected doesn't have to be done a second time for a single selection operation.
1048 You may pass 0 as \a details to indicate that you are not interested in those selection details.
1050 \see selectEvent, deselectEvent, QCustomPlot::setInteractions
1052 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1054 Q_UNUSED(pos)
1055 Q_UNUSED(onlySelectable)
1056 Q_UNUSED(details)
1057 return -1.0;
1060 /*! \internal
1062 Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
1063 passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to
1064 another one.
1066 Note that, unlike when passing a non-null parent plot in the constructor, this function does not
1067 make \a parentPlot the QObject-parent of this layerable. If you want this, call
1068 QObject::setParent(\a parentPlot) in addition to this function.
1070 Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
1071 make the layerable appear on the QCustomPlot.
1073 The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized
1074 so they can react accordingly (e.g. also initialize the parent plot of child layerables, like
1075 QCPLayout does).
1077 void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)
1079 if (mParentPlot)
1081 qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1082 return;
1085 if (!parentPlot)
1086 qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1088 mParentPlot = parentPlot;
1089 parentPlotInitialized(mParentPlot);
1092 /*! \internal
1094 Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not
1095 become the QObject-parent (for memory management) of this layerable.
1097 The parent layerable has influence on the return value of the \ref realVisibility method. Only
1098 layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be
1099 drawn.
1101 \see realVisibility
1103 void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)
1105 mParentLayerable = parentLayerable;
1108 /*! \internal
1110 Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
1111 the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
1112 false, the object will be appended.
1114 Returns true on success, i.e. if \a layer is a valid layer.
1116 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1118 if (layer && !mParentPlot)
1120 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1121 return false;
1123 if (layer && layer->parentPlot() != mParentPlot)
1125 qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1126 return false;
1129 QCPLayer *oldLayer = mLayer;
1130 if (mLayer)
1131 mLayer->removeChild(this);
1132 mLayer = layer;
1133 if (mLayer)
1134 mLayer->addChild(this, prepend);
1135 if (mLayer != oldLayer)
1136 emit layerChanged(mLayer);
1137 return true;
1140 /*! \internal
1142 Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a
1143 localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref
1144 QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
1145 controlled via \a overrideElement.
1147 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1149 if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1150 painter->setAntialiasing(false);
1151 else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1152 painter->setAntialiasing(true);
1153 else
1154 painter->setAntialiasing(localAntialiased);
1157 /*! \internal
1159 This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
1160 of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the
1161 parent plot is set at a later time.
1163 For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
1164 QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
1165 element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To
1166 propagate the parent plot to all the children of the hierarchy, the top level element then uses
1167 this function to pass the parent plot on to its child elements.
1169 The default implementation does nothing.
1171 \see initializeParentPlot
1173 void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)
1175 Q_UNUSED(parentPlot)
1178 /*! \internal
1180 Returns the selection category this layerable shall belong to. The selection category is used in
1181 conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and
1182 which aren't.
1184 Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref
1185 QCP::iSelectOther. This is what the default implementation returns.
1187 \see QCustomPlot::setInteractions
1189 QCP::Interaction QCPLayerable::selectionCategory() const
1191 return QCP::iSelectOther;
1194 /*! \internal
1196 Returns the clipping rectangle of this layerable object. By default, this is the viewport of the
1197 parent QCustomPlot. Specific subclasses may reimplement this function to provide different
1198 clipping rects.
1200 The returned clipping rect is set on the painter before the draw function of the respective
1201 object is called.
1203 QRect QCPLayerable::clipRect() const
1205 if (mParentPlot)
1206 return mParentPlot->viewport();
1207 else
1208 return QRect();
1211 /*! \internal
1213 This event is called when the layerable shall be selected, as a consequence of a click by the
1214 user. Subclasses should react to it by setting their selection state appropriately. The default
1215 implementation does nothing.
1217 \a event is the mouse event that caused the selection. \a additive indicates, whether the user
1218 was holding the multi-select-modifier while performing the selection (see \ref
1219 QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled
1220 (i.e. become selected when unselected and unselected when selected).
1222 Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e.
1223 returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).
1224 The \a details data you output from \ref selectTest is fed back via \a details here. You may
1225 use it to transport any kind of information from the selectTest to the possibly subsequent
1226 selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable
1227 that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need
1228 to do the calculation again to find out which part was actually clicked.
1230 \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must
1231 set the value either to true or false, depending on whether the selection state of this layerable
1232 was actually changed. For layerables that only are selectable as a whole and not in parts, this
1233 is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the
1234 selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the
1235 layerable was previously unselected and now is switched to the selected state.
1237 \see selectTest, deselectEvent
1239 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1241 Q_UNUSED(event)
1242 Q_UNUSED(additive)
1243 Q_UNUSED(details)
1244 Q_UNUSED(selectionStateChanged)
1247 /*! \internal
1249 This event is called when the layerable shall be deselected, either as consequence of a user
1250 interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by
1251 unsetting their selection appropriately.
1253 just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must
1254 return true or false when the selection state of this layerable has changed or not changed,
1255 respectively.
1257 \see selectTest, selectEvent
1259 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1261 Q_UNUSED(selectionStateChanged)
1265 ////////////////////////////////////////////////////////////////////////////////////////////////////
1266 //////////////////// QCPRange
1267 ////////////////////////////////////////////////////////////////////////////////////////////////////
1268 /*! \class QCPRange
1269 \brief Represents the range an axis is encompassing.
1271 contains a \a lower and \a upper double value and provides convenience input, output and
1272 modification functions.
1274 \see QCPAxis::setRange
1278 Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
1279 intervals would cause errors due to the 11-bit exponent of double precision numbers,
1280 corresponding to a minimum magnitude of roughly 1e-308.
1281 \see validRange, maxRange
1283 const double QCPRange::minRange = 1e-280;
1286 Maximum values (negative and positive) the range will accept in range-changing functions.
1287 Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
1288 corresponding to a maximum magnitude of roughly 1e308.
1289 Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
1290 be enough.
1291 \see validRange, minRange
1293 const double QCPRange::maxRange = 1e250;
1296 Constructs a range with \a lower and \a upper set to zero.
1298 QCPRange::QCPRange() :
1299 lower(0),
1300 upper(0)
1304 /*! \overload
1305 Constructs a range with the specified \a lower and \a upper values.
1307 QCPRange::QCPRange(double lower, double upper) :
1308 lower(lower),
1309 upper(upper)
1311 normalize();
1315 Returns the size of the range, i.e. \a upper-\a lower
1317 double QCPRange::size() const
1319 return upper-lower;
1323 Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1325 double QCPRange::center() const
1327 return (upper+lower)*0.5;
1331 Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
1332 are swapped.
1334 void QCPRange::normalize()
1336 if (lower > upper)
1337 qSwap(lower, upper);
1341 Expands this range such that \a otherRange is contained in the new range. It is assumed that both
1342 this range and \a otherRange are normalized (see \ref normalize).
1344 If \a otherRange is already inside the current range, this function does nothing.
1346 \see expanded
1348 void QCPRange::expand(const QCPRange &otherRange)
1350 if (lower > otherRange.lower)
1351 lower = otherRange.lower;
1352 if (upper < otherRange.upper)
1353 upper = otherRange.upper;
1358 Returns an expanded range that contains this and \a otherRange. It is assumed that both this
1359 range and \a otherRange are normalized (see \ref normalize).
1361 \see expand
1363 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1365 QCPRange result = *this;
1366 result.expand(otherRange);
1367 return result;
1371 Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
1372 the range won't span the positive and negative sign domain, i.e. contain zero. Further
1373 \a lower will always be numerically smaller (or equal) to \a upper.
1375 If the original range does span positive and negative sign domains or contains zero,
1376 the returned range will try to approximate the original range as good as possible.
1377 If the positive interval of the original range is wider than the negative interval, the
1378 returned range will only contain the positive interval, with lower bound set to \a rangeFac or
1379 \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
1380 is wider than the positive interval, this time by changing the \a upper bound.
1382 QCPRange QCPRange::sanitizedForLogScale() const
1384 double rangeFac = 1e-3;
1385 QCPRange sanitizedRange(lower, upper);
1386 sanitizedRange.normalize();
1387 // can't have range spanning negative and positive values in log plot, so change range to fix it
1388 //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1389 if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1391 // case lower is 0
1392 if (rangeFac < sanitizedRange.upper*rangeFac)
1393 sanitizedRange.lower = rangeFac;
1394 else
1395 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1396 } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1397 else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1399 // case upper is 0
1400 if (-rangeFac > sanitizedRange.lower*rangeFac)
1401 sanitizedRange.upper = -rangeFac;
1402 else
1403 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1404 } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1406 // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1407 if (-sanitizedRange.lower > sanitizedRange.upper)
1409 // negative is wider, do same as in case upper is 0
1410 if (-rangeFac > sanitizedRange.lower*rangeFac)
1411 sanitizedRange.upper = -rangeFac;
1412 else
1413 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1414 } else
1416 // positive is wider, do same as in case lower is 0
1417 if (rangeFac < sanitizedRange.upper*rangeFac)
1418 sanitizedRange.lower = rangeFac;
1419 else
1420 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1423 // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1424 return sanitizedRange;
1428 Returns a sanitized version of the range. Sanitized means for linear scales, that
1429 \a lower will always be numerically smaller (or equal) to \a upper.
1431 QCPRange QCPRange::sanitizedForLinScale() const
1433 QCPRange sanitizedRange(lower, upper);
1434 sanitizedRange.normalize();
1435 return sanitizedRange;
1439 Returns true when \a value lies within or exactly on the borders of the range.
1441 bool QCPRange::contains(double value) const
1443 return value >= lower && value <= upper;
1447 Checks, whether the specified range is within valid bounds, which are defined
1448 as QCPRange::maxRange and QCPRange::minRange.
1449 A valid range means:
1450 \li range bounds within -maxRange and maxRange
1451 \li range size above minRange
1452 \li range size below maxRange
1454 bool QCPRange::validRange(double lower, double upper)
1457 return (lower > -maxRange &&
1458 upper < maxRange &&
1459 qAbs(lower-upper) > minRange &&
1460 (lower < -minRange || lower > minRange) &&
1461 (upper < -minRange || upper > minRange));
1463 return (lower > -maxRange &&
1464 upper < maxRange &&
1465 qAbs(lower-upper) > minRange &&
1466 qAbs(lower-upper) < maxRange);
1470 \overload
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)
1481 return (range.lower > -maxRange &&
1482 range.upper < maxRange &&
1483 qAbs(range.lower-range.upper) > minRange &&
1484 qAbs(range.lower-range.upper) < maxRange &&
1485 (range.lower < -minRange || range.lower > minRange) &&
1486 (range.upper < -minRange || range.upper > minRange));
1488 return (range.lower > -maxRange &&
1489 range.upper < maxRange &&
1490 qAbs(range.lower-range.upper) > minRange &&
1491 qAbs(range.lower-range.upper) < maxRange);
1495 ////////////////////////////////////////////////////////////////////////////////////////////////////
1496 //////////////////// QCPMarginGroup
1497 ////////////////////////////////////////////////////////////////////////////////////////////////////
1499 /*! \class QCPMarginGroup
1500 \brief A margin group allows synchronization of margin sides if working with multiple layout elements.
1502 QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that
1503 they will all have the same size, based on the largest required margin in the group.
1506 \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
1509 In certain situations it is desirable that margins at specific sides are synchronized across
1510 layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will
1511 provide a cleaner look to the user if the left and right margins of the two axis rects are of the
1512 same size. The left axis of the top axis rect will then be at the same horizontal position as the
1513 left axis of the lower axis rect, making them appear aligned. The same applies for the right
1514 axes. This is what QCPMarginGroup makes possible.
1516 To add/remove a specific side of a layout element to/from a margin group, use the \ref
1517 QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call
1518 \ref clear, or just delete the margin group.
1520 \section QCPMarginGroup-example Example
1522 First create a margin group:
1523 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1
1524 Then set this group on the layout element sides:
1525 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2
1526 Here, we've used the first two axis rects of the plot and synchronized their left margins with
1527 each other and their right margins with each other.
1530 /* start documentation of inline functions */
1532 /*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const
1534 Returns a list of all layout elements that have their margin \a side associated with this margin
1535 group.
1538 /* end documentation of inline functions */
1541 Creates a new QCPMarginGroup instance in \a parentPlot.
1543 QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :
1544 QObject(parentPlot),
1545 mParentPlot(parentPlot)
1547 mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1548 mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1549 mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1550 mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1553 QCPMarginGroup::~QCPMarginGroup()
1555 clear();
1559 Returns whether this margin group is empty. If this function returns true, no layout elements use
1560 this margin group to synchronize margin sides.
1562 bool QCPMarginGroup::isEmpty() const
1564 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1565 while (it.hasNext())
1567 it.next();
1568 if (!it.value().isEmpty())
1569 return false;
1571 return true;
1575 Clears this margin group. The synchronization of the margin sides that use this margin group is
1576 lifted and they will use their individual margin sizes again.
1578 void QCPMarginGroup::clear()
1580 // make all children remove themselves from this margin group:
1581 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1582 while (it.hasNext())
1584 it.next();
1585 const QList<QCPLayoutElement*> elements = it.value();
1586 for (int i=elements.size()-1; i>=0; --i)
1587 elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1591 /*! \internal
1593 Returns the synchronized common margin for \a side. This is the margin value that will be used by
1594 the layout element on the respective side, if it is part of this margin group.
1596 The common margin is calculated by requesting the automatic margin (\ref
1597 QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin
1598 group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into
1599 account, too.)
1601 int QCPMarginGroup::commonMargin(QCP::MarginSide side) const
1603 // query all automatic margins of the layout elements in this margin group side and find maximum:
1604 int result = 0;
1605 const QList<QCPLayoutElement*> elements = mChildren.value(side);
1606 for (int i=0; i<elements.size(); ++i)
1608 if (!elements.at(i)->autoMargins().testFlag(side))
1609 continue;
1610 int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1611 if (m > result)
1612 result = m;
1614 return result;
1617 /*! \internal
1619 Adds \a element to the internal list of child elements, for the margin \a side.
1621 This function does not modify the margin group property of \a element.
1623 void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)
1625 if (!mChildren[side].contains(element))
1626 mChildren[side].append(element);
1627 else
1628 qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1631 /*! \internal
1633 Removes \a element from the internal list of child elements, for the margin \a side.
1635 This function does not modify the margin group property of \a element.
1637 void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)
1639 if (!mChildren[side].removeOne(element))
1640 qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1644 ////////////////////////////////////////////////////////////////////////////////////////////////////
1645 //////////////////// QCPLayoutElement
1646 ////////////////////////////////////////////////////////////////////////////////////////////////////
1648 /*! \class QCPLayoutElement
1649 \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system".
1651 This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.
1653 A Layout element is a rectangular object which can be placed in layouts. It has an outer rect
1654 (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference
1655 between outer and inner rect is called its margin. The margin can either be set to automatic or
1656 manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be
1657 set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic,
1658 the layout element subclass will control the value itself (via \ref calculateAutoMargin).
1660 Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level
1661 layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref
1662 QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested.
1664 Thus in QCustomPlot one can divide layout elements into two categories: The ones that are
1665 invisible by themselves, because they don't draw anything. Their only purpose is to manage the
1666 position and size of other layout elements. This category of layout elements usually use
1667 QCPLayout as base class. Then there is the category of layout elements which actually draw
1668 something. For example, QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does
1669 not necessarily mean that the latter category can't have child layout elements. QCPLegend for
1670 instance, actually derives from QCPLayoutGrid and the individual legend items are child layout
1671 elements in the grid layout.
1674 /* start documentation of inline functions */
1676 /*! \fn QCPLayout *QCPLayoutElement::layout() const
1678 Returns the parent layout of this layout element.
1681 /*! \fn QRect QCPLayoutElement::rect() const
1683 Returns the inner rect of this layout element. The inner rect is the outer rect (\ref
1684 setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins).
1686 In some cases, the area between outer and inner rect is left blank. In other cases the margin
1687 area is used to display peripheral graphics while the main content is in the inner rect. This is
1688 where automatic margin calculation becomes interesting because it allows the layout element to
1689 adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
1690 draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if
1691 \ref setAutoMargins is enabled) according to the space required by the labels of the axes.
1694 /*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
1696 This event is called, if the mouse was pressed while being inside the outer rect of this layout
1697 element.
1700 /*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
1702 This event is called, if the mouse is moved inside the outer rect of this layout element.
1705 /*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
1707 This event is called, if the mouse was previously pressed inside the outer rect of this layout
1708 element and is now released.
1711 /*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
1713 This event is called, if the mouse is double-clicked inside the outer rect of this layout
1714 element.
1717 /*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
1719 This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this
1720 layout element.
1723 /* end documentation of inline functions */
1726 Creates an instance of QCPLayoutElement and sets default values.
1728 QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
1729 QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1730 mParentLayout(0),
1731 mMinimumSize(),
1732 mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1733 mRect(0, 0, 0, 0),
1734 mOuterRect(0, 0, 0, 0),
1735 mMargins(0, 0, 0, 0),
1736 mMinimumMargins(0, 0, 0, 0),
1737 mAutoMargins(QCP::msAll)
1741 QCPLayoutElement::~QCPLayoutElement()
1743 setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1744 // unregister at layout:
1745 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
1746 mParentLayout->take(this);
1750 Sets the outer rect of this layout element. If the layout element is inside a layout, the layout
1751 sets the position and size of this layout element using this function.
1753 Calling this function externally has no effect, since the layout will overwrite any changes to
1754 the outer rect upon the next replot.
1756 The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect.
1758 \see rect
1760 void QCPLayoutElement::setOuterRect(const QRect &rect)
1762 if (mOuterRect != rect)
1764 mOuterRect = rect;
1765 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1770 Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all
1771 sides, this function is used to manually set the margin on those sides. Sides that are still set
1772 to be handled automatically are ignored and may have any value in \a margins.
1774 The margin is the distance between the outer rect (controlled by the parent layout via \ref
1775 setOuterRect) and the inner \ref rect (which usually contains the main content of this layout
1776 element).
1778 \see setAutoMargins
1780 void QCPLayoutElement::setMargins(const QMargins &margins)
1782 if (mMargins != margins)
1784 mMargins = margins;
1785 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1790 If \ref setAutoMargins is enabled on some or all margins, this function is used to provide
1791 minimum values for those margins.
1793 The minimum values are not enforced on margin sides that were set to be under manual control via
1794 \ref setAutoMargins.
1796 \see setAutoMargins
1798 void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1800 if (mMinimumMargins != margins)
1802 mMinimumMargins = margins;
1807 Sets on which sides the margin shall be calculated automatically. If a side is calculated
1808 automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is
1809 set to be controlled manually, the value may be specified with \ref setMargins.
1811 Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref
1812 setMarginGroup), to synchronize (align) it with other layout elements in the plot.
1814 \see setMinimumMargins, setMargins
1816 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1818 mAutoMargins = sides;
1822 Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to
1823 respect the \a size here by changing row/column sizes in the layout accordingly.
1825 If the parent layout size is not sufficient to satisfy all minimum size constraints of its child
1826 layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot
1827 propagates the layout's size constraints to the outside by setting its own minimum QWidget size
1828 accordingly, so violations of \a size should be exceptions.
1830 void QCPLayoutElement::setMinimumSize(const QSize &size)
1832 if (mMinimumSize != size)
1834 mMinimumSize = size;
1835 if (mParentLayout)
1836 mParentLayout->sizeConstraintsChanged();
1840 /*! \overload
1842 Sets the minimum size for the inner \ref rect of this layout element.
1844 void QCPLayoutElement::setMinimumSize(int width, int height)
1846 setMinimumSize(QSize(width, height));
1850 Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to
1851 respect the \a size here by changing row/column sizes in the layout accordingly.
1853 void QCPLayoutElement::setMaximumSize(const QSize &size)
1855 if (mMaximumSize != size)
1857 mMaximumSize = size;
1858 if (mParentLayout)
1859 mParentLayout->sizeConstraintsChanged();
1863 /*! \overload
1865 Sets the maximum size for the inner \ref rect of this layout element.
1867 void QCPLayoutElement::setMaximumSize(int width, int height)
1869 setMaximumSize(QSize(width, height));
1873 Sets the margin \a group of the specified margin \a sides.
1875 Margin groups allow synchronizing specified margins across layout elements, see the documentation
1876 of \ref QCPMarginGroup.
1878 To unset the margin group of \a sides, set \a group to 0.
1880 Note that margin groups only work for margin sides that are set to automatic (\ref
1881 setAutoMargins).
1883 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1885 QVector<QCP::MarginSide> sideVector;
1886 if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1887 if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1888 if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1889 if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1891 for (int i=0; i<sideVector.size(); ++i)
1893 QCP::MarginSide side = sideVector.at(i);
1894 if (marginGroup(side) != group)
1896 QCPMarginGroup *oldGroup = marginGroup(side);
1897 if (oldGroup) // unregister at old group
1898 oldGroup->removeChild(side, this);
1900 if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1902 mMarginGroups.remove(side);
1903 } else // setting to a new group
1905 mMarginGroups[side] = group;
1906 group->addChild(side, this);
1913 Updates the layout element and sub-elements. This function is automatically called before every
1914 replot by the parent layout element. It is called multiple times, once for every \ref
1915 UpdatePhase. The phases are run through in the order of the enum values. For details about what
1916 happens at the different phases, see the documentation of \ref UpdatePhase.
1918 Layout elements that have child elements should call the \ref update method of their child
1919 elements, and pass the current \a phase unchanged.
1921 The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
1922 Subclasses should make sure to call the base class implementation.
1924 void QCPLayoutElement::update(UpdatePhase phase)
1926 if (phase == upMargins)
1928 if (mAutoMargins != QCP::msNone)
1930 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1931 QMargins newMargins = mMargins;
1932 QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
1933 foreach (QCP::MarginSide side, allMarginSides)
1935 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1937 if (mMarginGroups.contains(side))
1938 QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1939 else
1940 QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1941 // apply minimum margin restrictions:
1942 if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1943 QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1946 setMargins(newMargins);
1952 Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
1954 if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this
1955 function to determine the minimum allowed size of this layout element. (A manual minimum size is
1956 considered set if it is non-zero.)
1958 QSize QCPLayoutElement::minimumSizeHint() const
1960 return mMinimumSize;
1964 Returns the maximum size this layout element (the inner \ref rect) may be expanded to.
1966 if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this
1967 function to determine the maximum allowed size of this layout element. (A manual maximum size is
1968 considered set if it is smaller than Qt's QWIDGETSIZE_MAX.)
1970 QSize QCPLayoutElement::maximumSizeHint() const
1972 return mMaximumSize;
1976 Returns a list of all child elements in this layout element. If \a recursive is true, all
1977 sub-child elements are included in the list, too.
1979 \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
1980 empty cells which yield 0 at the respective index.)
1982 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
1984 Q_UNUSED(recursive)
1985 return QList<QCPLayoutElement*>();
1989 Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer
1990 rect, this method returns a value corresponding to 0.99 times the parent plot's selection
1991 tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is
1992 true, -1.0 is returned.
1994 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
1996 QCPLayoutElement subclasses may reimplement this method to provide more specific selection test
1997 behaviour.
1999 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2001 Q_UNUSED(details)
2003 if (onlySelectable)
2004 return -1;
2006 if (QRectF(mOuterRect).contains(pos))
2008 if (mParentPlot)
2009 return mParentPlot->selectionTolerance()*0.99;
2010 else
2012 qDebug() << Q_FUNC_INFO << "parent plot not defined";
2013 return -1;
2015 } else
2016 return -1;
2019 /*! \internal
2021 propagates the parent plot initialization to all child elements, by calling \ref
2022 QCPLayerable::initializeParentPlot on them.
2024 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2026 foreach (QCPLayoutElement* el, elements(false))
2028 if (!el->parentPlot())
2029 el->initializeParentPlot(parentPlot);
2033 /*! \internal
2035 Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
2036 side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
2037 returned value will not be smaller than the specified minimum margin.
2039 The default implementation just returns the respective manual margin (\ref setMargins) or the
2040 minimum margin, whichever is larger.
2042 int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)
2044 return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
2047 ////////////////////////////////////////////////////////////////////////////////////////////////////
2048 //////////////////// QCPLayout
2049 ////////////////////////////////////////////////////////////////////////////////////////////////////
2051 /*! \class QCPLayout
2052 \brief The abstract base class for layouts
2054 This is an abstract base class for layout elements whose main purpose is to define the position
2055 and size of other child layout elements. In most cases, layouts don't draw anything themselves
2056 (but there are exceptions to this, e.g. QCPLegend).
2058 QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.
2060 QCPLayout introduces a common interface for accessing and manipulating the child elements. Those
2061 functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref
2062 simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions
2063 to this interface which are more specialized to the form of the layout. For example, \ref
2064 QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid
2065 more conveniently.
2067 Since this is an abstract base class, you can't instantiate it directly. Rather use one of its
2068 subclasses like QCPLayoutGrid or QCPLayoutInset.
2070 For a general introduction to the layout system, see the dedicated documentation page \ref
2071 thelayoutsystem "The Layout System".
2074 /* start documentation of pure virtual functions */
2076 /*! \fn virtual int QCPLayout::elementCount() const = 0
2078 Returns the number of elements/cells in the layout.
2080 \see elements, elementAt
2083 /*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
2085 Returns the element in the cell with the given \a index. If \a index is invalid, returns 0.
2087 Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
2088 QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check
2089 whether a cell is empty or not.
2091 \see elements, elementCount, takeAt
2094 /*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
2096 Removes the element with the given \a index from the layout and returns it.
2098 If the \a index is invalid or the cell with that index is empty, returns 0.
2100 Note that some layouts don't remove the respective cell right away but leave an empty cell after
2101 successful removal of the layout element. To collapse empty cells, use \ref simplify.
2103 \see elementAt, take
2106 /*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
2108 Removes the specified \a element from the layout and returns true on success.
2110 If the \a element isn't in this layout, returns false.
2112 Note that some layouts don't remove the respective cell right away but leave an empty cell after
2113 successful removal of the layout element. To collapse empty cells, use \ref simplify.
2115 \see takeAt
2118 /* end documentation of pure virtual functions */
2121 Creates an instance of QCPLayout and sets default values. Note that since QCPLayout
2122 is an abstract base class, it can't be instantiated directly.
2124 QCPLayout::QCPLayout()
2129 First calls the QCPLayoutElement::update base class implementation to update the margins on this
2130 layout.
2132 Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells.
2134 Finally, \ref update is called on all child elements.
2136 void QCPLayout::update(UpdatePhase phase)
2138 QCPLayoutElement::update(phase);
2140 // set child element rects according to layout:
2141 if (phase == upLayout)
2142 updateLayout();
2144 // propagate update call to child elements:
2145 const int elCount = elementCount();
2146 for (int i=0; i<elCount; ++i)
2148 if (QCPLayoutElement *el = elementAt(i))
2149 el->update(phase);
2153 /* inherits documentation from base class */
2154 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2156 const int c = elementCount();
2157 QList<QCPLayoutElement*> result;
2158 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2159 result.reserve(c);
2160 #endif
2161 for (int i=0; i<c; ++i)
2162 result.append(elementAt(i));
2163 if (recursive)
2165 for (int i=0; i<c; ++i)
2167 if (result.at(i))
2168 result << result.at(i)->elements(recursive);
2171 return result;
2175 Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the
2176 default implementation does nothing.
2178 Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit
2179 simplification while QCPLayoutGrid does.
2181 void QCPLayout::simplify()
2186 Removes and deletes the element at the provided \a index. Returns true on success. If \a index is
2187 invalid or points to an empty cell, returns false.
2189 This function internally uses \ref takeAt to remove the element from the layout and then deletes
2190 the returned element. Note that some layouts don't remove the respective cell right away but leave an
2191 empty cell after successful removal of the layout element. To collapse empty cells, use \ref
2192 simplify.
2194 \see remove, takeAt
2196 bool QCPLayout::removeAt(int index)
2198 if (QCPLayoutElement *el = takeAt(index))
2200 delete el;
2201 return true;
2202 } else
2203 return false;
2207 Removes and deletes the provided \a element. Returns true on success. If \a element is not in the
2208 layout, returns false.
2210 This function internally uses \ref takeAt to remove the element from the layout and then deletes
2211 the element. Note that some layouts don't remove the respective cell right away but leave an
2212 empty cell after successful removal of the layout element. To collapse empty cells, use \ref
2213 simplify.
2215 \see removeAt, take
2217 bool QCPLayout::remove(QCPLayoutElement *element)
2219 if (take(element))
2221 delete element;
2222 return true;
2223 } else
2224 return false;
2228 Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure
2229 all empty cells are collapsed.
2231 \see remove, removeAt
2233 void QCPLayout::clear()
2235 for (int i=elementCount()-1; i>=0; --i)
2237 if (elementAt(i))
2238 removeAt(i);
2240 simplify();
2244 Subclasses call this method to report changed (minimum/maximum) size constraints.
2246 If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref
2247 sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of
2248 QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,
2249 it may update itself and resize cells accordingly.
2251 void QCPLayout::sizeConstraintsChanged() const
2253 if (QWidget *w = qobject_cast<QWidget*>(parent()))
2254 w->updateGeometry();
2255 else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2256 l->sizeConstraintsChanged();
2259 /*! \internal
2261 Subclasses reimplement this method to update the position and sizes of the child elements/cells
2262 via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing.
2264 The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay
2265 within that rect.
2267 \ref getSectionSizes may help with the reimplementation of this function.
2269 \see update
2271 void QCPLayout::updateLayout()
2276 /*! \internal
2278 Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the
2279 \ref QCPLayerable::parentLayerable and the QObject parent to this layout.
2281 Further, if \a el didn't previously have a parent plot, calls \ref
2282 QCPLayerable::initializeParentPlot on \a el to set the paret plot.
2284 This method is used by subclass specific methods that add elements to the layout. Note that this
2285 method only changes properties in \a el. The removal from the old layout and the insertion into
2286 the new layout must be done additionally.
2288 void QCPLayout::adoptElement(QCPLayoutElement *el)
2290 if (el)
2292 el->mParentLayout = this;
2293 el->setParentLayerable(this);
2294 el->setParent(this);
2295 if (!el->parentPlot())
2296 el->initializeParentPlot(mParentPlot);
2297 } else
2298 qDebug() << Q_FUNC_INFO << "Null element passed";
2301 /*! \internal
2303 Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout
2304 and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent
2305 QCustomPlot.
2307 This method is used by subclass specific methods that remove elements from the layout (e.g. \ref
2308 take or \ref takeAt). Note that this method only changes properties in \a el. The removal from
2309 the old layout must be done additionally.
2311 void QCPLayout::releaseElement(QCPLayoutElement *el)
2313 if (el)
2315 el->mParentLayout = 0;
2316 el->setParentLayerable(0);
2317 el->setParent(mParentPlot);
2318 // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2319 } else
2320 qDebug() << Q_FUNC_INFO << "Null element passed";
2323 /*! \internal
2325 This is a helper function for the implementation of \ref updateLayout in subclasses.
2327 It calculates the sizes of one-dimensional sections with provided constraints on maximum section
2328 sizes, minimum section sizes, relative stretch factors and the final total size of all sections.
2330 The QVector entries refer to the sections. Thus all QVectors must have the same size.
2332 \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size
2333 imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
2335 \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size
2336 imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than
2337 \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,
2338 not exceeding the allowed total size is taken to be more important than not going below minimum
2339 section sizes.)
2341 \a stretchFactors give the relative proportions of the sections to each other. If all sections
2342 shall be scaled equally, set all values equal. If the first section shall be double the size of
2343 each individual other section, set the first number of \a stretchFactors to double the value of
2344 the other individual values (e.g. {2, 1, 1, 1}).
2346 \a totalSize is the value that the final section sizes will add up to. Due to rounding, the
2347 actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,
2348 you could distribute the remaining difference on the sections.
2350 The return value is a QVector containing the section sizes.
2352 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2354 if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2356 qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2357 return QVector<int>();
2359 if (stretchFactors.isEmpty())
2360 return QVector<int>();
2361 int sectionCount = stretchFactors.size();
2362 QVector<double> sectionSizes(sectionCount);
2363 // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2364 int minSizeSum = 0;
2365 for (int i=0; i<sectionCount; ++i)
2366 minSizeSum += minSizes.at(i);
2367 if (totalSize < minSizeSum)
2369 // new stretch factors are minimum sizes and minimum sizes are set to zero:
2370 for (int i=0; i<sectionCount; ++i)
2372 stretchFactors[i] = minSizes.at(i);
2373 minSizes[i] = 0;
2377 QList<int> minimumLockedSections;
2378 QList<int> unfinishedSections;
2379 for (int i=0; i<sectionCount; ++i)
2380 unfinishedSections.append(i);
2381 double freeSize = totalSize;
2383 int outerIterations = 0;
2384 while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2386 ++outerIterations;
2387 int innerIterations = 0;
2388 while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2390 ++innerIterations;
2391 // find section that hits its maximum next:
2392 int nextId = -1;
2393 double nextMax = 1e12;
2394 for (int i=0; i<unfinishedSections.size(); ++i)
2396 int secId = unfinishedSections.at(i);
2397 double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2398 if (hitsMaxAt < nextMax)
2400 nextMax = hitsMaxAt;
2401 nextId = secId;
2404 // 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
2405 // actually hits its maximum, without exceeding the total size when we add up all sections)
2406 double stretchFactorSum = 0;
2407 for (int i=0; i<unfinishedSections.size(); ++i)
2408 stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2409 double nextMaxLimit = freeSize/stretchFactorSum;
2410 if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2412 for (int i=0; i<unfinishedSections.size(); ++i)
2414 sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2415 freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2417 unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2418 } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2420 for (int i=0; i<unfinishedSections.size(); ++i)
2421 sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2422 unfinishedSections.clear();
2425 if (innerIterations == sectionCount*2)
2426 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2428 // now check whether the resulting section sizes violate minimum restrictions:
2429 bool foundMinimumViolation = false;
2430 for (int i=0; i<sectionSizes.size(); ++i)
2432 if (minimumLockedSections.contains(i))
2433 continue;
2434 if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2436 sectionSizes[i] = minSizes.at(i); // set it to minimum
2437 foundMinimumViolation = true; // make sure we repeat the whole optimization process
2438 minimumLockedSections.append(i);
2441 if (foundMinimumViolation)
2443 freeSize = totalSize;
2444 for (int i=0; i<sectionCount; ++i)
2446 if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2447 unfinishedSections.append(i);
2448 else
2449 freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2451 // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2452 for (int i=0; i<unfinishedSections.size(); ++i)
2453 sectionSizes[unfinishedSections.at(i)] = 0;
2456 if (outerIterations == sectionCount*2)
2457 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2459 QVector<int> result(sectionCount);
2460 for (int i=0; i<sectionCount; ++i)
2461 result[i] = qRound(sectionSizes.at(i));
2462 return result;
2466 ////////////////////////////////////////////////////////////////////////////////////////////////////
2467 //////////////////// QCPLayoutGrid
2468 ////////////////////////////////////////////////////////////////////////////////////////////////////
2470 /*! \class QCPLayoutGrid
2471 \brief A layout that arranges child elements in a grid
2473 Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor,
2474 \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing).
2476 Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or
2477 column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref
2478 hasElement, that element can be retrieved with \ref element. If rows and columns that only have
2479 empty cells shall be removed, call \ref simplify. Removal of elements is either done by just
2480 adding the element to a different layout or by using the QCPLayout interface \ref take or \ref
2481 remove.
2483 Row and column insertion can be performed with \ref insertRow and \ref insertColumn.
2487 Creates an instance of QCPLayoutGrid and sets default values.
2489 QCPLayoutGrid::QCPLayoutGrid() :
2490 mColumnSpacing(5),
2491 mRowSpacing(5)
2495 QCPLayoutGrid::~QCPLayoutGrid()
2497 // clear all child layout elements. This is important because only the specific layouts know how
2498 // to handle removing elements (clear calls virtual removeAt method to do that).
2499 clear();
2503 Returns the element in the cell in \a row and \a column.
2505 Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug
2506 message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
2508 \see addElement, hasElement
2510 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2512 if (row >= 0 && row < mElements.size())
2514 if (column >= 0 && column < mElements.first().size())
2516 if (QCPLayoutElement *result = mElements.at(row).at(column))
2517 return result;
2518 else
2519 qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2520 } else
2521 qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2522 } else
2523 qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2524 return 0;
2528 Returns the number of rows in the layout.
2530 \see columnCount
2532 int QCPLayoutGrid::rowCount() const
2534 return mElements.size();
2538 Returns the number of columns in the layout.
2540 \see rowCount
2542 int QCPLayoutGrid::columnCount() const
2544 if (mElements.size() > 0)
2545 return mElements.first().size();
2546 else
2547 return 0;
2551 Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it
2552 is first removed from there. If \a row or \a column don't exist yet, the layout is expanded
2553 accordingly.
2555 Returns true if the element was added successfully, i.e. if the cell at \a row and \a column
2556 didn't already have an element.
2558 \see element, hasElement, take, remove
2560 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2562 if (element)
2564 if (!hasElement(row, column))
2566 if (element->layout()) // remove from old layout first
2567 element->layout()->take(element);
2568 expandTo(row+1, column+1);
2569 mElements[row][column] = element;
2570 adoptElement(element);
2571 return true;
2572 } else
2573 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2574 } else
2575 qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2576 return false;
2580 Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't
2581 empty.
2583 \see element
2585 bool QCPLayoutGrid::hasElement(int row, int column)
2587 if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2588 return mElements.at(row).at(column);
2589 else
2590 return false;
2594 Sets the stretch \a factor of \a column.
2596 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2597 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2598 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2600 The default stretch factor of newly created rows/columns is 1.
2602 \see setColumnStretchFactors, setRowStretchFactor
2604 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2606 if (column >= 0 && column < columnCount())
2608 if (factor > 0)
2609 mColumnStretchFactors[column] = factor;
2610 else
2611 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2612 } else
2613 qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2617 Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount.
2619 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2620 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2621 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2623 The default stretch factor of newly created rows/columns is 1.
2625 \see setColumnStretchFactor, setRowStretchFactors
2627 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2629 if (factors.size() == mColumnStretchFactors.size())
2631 mColumnStretchFactors = factors;
2632 for (int i=0; i<mColumnStretchFactors.size(); ++i)
2634 if (mColumnStretchFactors.at(i) <= 0)
2636 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2637 mColumnStretchFactors[i] = 1;
2640 } else
2641 qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2645 Sets the stretch \a factor of \a row.
2647 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2648 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2649 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2651 The default stretch factor of newly created rows/columns is 1.
2653 \see setColumnStretchFactors, setRowStretchFactor
2655 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2657 if (row >= 0 && row < rowCount())
2659 if (factor > 0)
2660 mRowStretchFactors[row] = factor;
2661 else
2662 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2663 } else
2664 qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2668 Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount.
2670 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2671 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2672 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2674 The default stretch factor of newly created rows/columns is 1.
2676 \see setRowStretchFactor, setColumnStretchFactors
2678 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2680 if (factors.size() == mRowStretchFactors.size())
2682 mRowStretchFactors = factors;
2683 for (int i=0; i<mRowStretchFactors.size(); ++i)
2685 if (mRowStretchFactors.at(i) <= 0)
2687 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2688 mRowStretchFactors[i] = 1;
2691 } else
2692 qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2696 Sets the gap that is left blank between columns to \a pixels.
2698 \see setRowSpacing
2700 void QCPLayoutGrid::setColumnSpacing(int pixels)
2702 mColumnSpacing = pixels;
2706 Sets the gap that is left blank between rows to \a pixels.
2708 \see setColumnSpacing
2710 void QCPLayoutGrid::setRowSpacing(int pixels)
2712 mRowSpacing = pixels;
2716 Expands the layout to have \a newRowCount rows and \a newColumnCount columns. So the last valid
2717 row index will be \a newRowCount-1, the last valid column index will be \a newColumnCount-1.
2719 If the current column/row count is already larger or equal to \a newColumnCount/\a newRowCount,
2720 this function does nothing in that dimension.
2722 Newly created cells are empty, new rows and columns have the stretch factor 1.
2724 Note that upon a call to \ref addElement, the layout is expanded automatically to contain the
2725 specified row and column, using this function.
2727 \see simplify
2729 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2731 // add rows as necessary:
2732 while (rowCount() < newRowCount)
2734 mElements.append(QList<QCPLayoutElement*>());
2735 mRowStretchFactors.append(1);
2737 // go through rows and expand columns as necessary:
2738 int newColCount = qMax(columnCount(), newColumnCount);
2739 for (int i=0; i<rowCount(); ++i)
2741 while (mElements.at(i).size() < newColCount)
2742 mElements[i].append(0);
2744 while (mColumnStretchFactors.size() < newColCount)
2745 mColumnStretchFactors.append(1);
2749 Inserts a new row with empty cells at the row index \a newIndex. Valid values for \a newIndex
2750 range from 0 (inserts a row at the top) to \a rowCount (appends a row at the bottom).
2752 \see insertColumn
2754 void QCPLayoutGrid::insertRow(int newIndex)
2756 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2758 expandTo(1, 1);
2759 return;
2762 if (newIndex < 0)
2763 newIndex = 0;
2764 if (newIndex > rowCount())
2765 newIndex = rowCount();
2767 mRowStretchFactors.insert(newIndex, 1);
2768 QList<QCPLayoutElement*> newRow;
2769 for (int col=0; col<columnCount(); ++col)
2770 newRow.append((QCPLayoutElement*)0);
2771 mElements.insert(newIndex, newRow);
2775 Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a
2776 newIndex range from 0 (inserts a row at the left) to \a rowCount (appends a row at the right).
2778 \see insertRow
2780 void QCPLayoutGrid::insertColumn(int newIndex)
2782 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2784 expandTo(1, 1);
2785 return;
2788 if (newIndex < 0)
2789 newIndex = 0;
2790 if (newIndex > columnCount())
2791 newIndex = columnCount();
2793 mColumnStretchFactors.insert(newIndex, 1);
2794 for (int row=0; row<rowCount(); ++row)
2795 mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2798 /* inherits documentation from base class */
2799 void QCPLayoutGrid::updateLayout()
2801 QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2802 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2803 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2805 int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2806 int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2807 QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2808 QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2810 // go through cells and set rects accordingly:
2811 int yOffset = mRect.top();
2812 for (int row=0; row<rowCount(); ++row)
2814 if (row > 0)
2815 yOffset += rowHeights.at(row-1)+mRowSpacing;
2816 int xOffset = mRect.left();
2817 for (int col=0; col<columnCount(); ++col)
2819 if (col > 0)
2820 xOffset += colWidths.at(col-1)+mColumnSpacing;
2821 if (mElements.at(row).at(col))
2822 mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2827 /* inherits documentation from base class */
2828 int QCPLayoutGrid::elementCount() const
2830 return rowCount()*columnCount();
2833 /* inherits documentation from base class */
2834 QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
2836 if (index >= 0 && index < elementCount())
2837 return mElements.at(index / columnCount()).at(index % columnCount());
2838 else
2839 return 0;
2842 /* inherits documentation from base class */
2843 QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
2845 if (QCPLayoutElement *el = elementAt(index))
2847 releaseElement(el);
2848 mElements[index / columnCount()][index % columnCount()] = 0;
2849 return el;
2850 } else
2852 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2853 return 0;
2857 /* inherits documentation from base class */
2858 bool QCPLayoutGrid::take(QCPLayoutElement *element)
2860 if (element)
2862 for (int i=0; i<elementCount(); ++i)
2864 if (elementAt(i) == element)
2866 takeAt(i);
2867 return true;
2870 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2871 } else
2872 qDebug() << Q_FUNC_INFO << "Can't take null element";
2873 return false;
2876 /* inherits documentation from base class */
2877 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2879 QList<QCPLayoutElement*> result;
2880 int colC = columnCount();
2881 int rowC = rowCount();
2882 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2883 result.reserve(colC*rowC);
2884 #endif
2885 for (int row=0; row<rowC; ++row)
2887 for (int col=0; col<colC; ++col)
2889 result.append(mElements.at(row).at(col));
2892 if (recursive)
2894 int c = result.size();
2895 for (int i=0; i<c; ++i)
2897 if (result.at(i))
2898 result << result.at(i)->elements(recursive);
2901 return result;
2905 Simplifies the layout by collapsing rows and columns which only contain empty cells.
2907 void QCPLayoutGrid::simplify()
2909 // remove rows with only empty cells:
2910 for (int row=rowCount()-1; row>=0; --row)
2912 bool hasElements = false;
2913 for (int col=0; col<columnCount(); ++col)
2915 if (mElements.at(row).at(col))
2917 hasElements = true;
2918 break;
2921 if (!hasElements)
2923 mRowStretchFactors.removeAt(row);
2924 mElements.removeAt(row);
2925 if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2926 mColumnStretchFactors.clear();
2930 // remove columns with only empty cells:
2931 for (int col=columnCount()-1; col>=0; --col)
2933 bool hasElements = false;
2934 for (int row=0; row<rowCount(); ++row)
2936 if (mElements.at(row).at(col))
2938 hasElements = true;
2939 break;
2942 if (!hasElements)
2944 mColumnStretchFactors.removeAt(col);
2945 for (int row=0; row<rowCount(); ++row)
2946 mElements[row].removeAt(col);
2951 /* inherits documentation from base class */
2952 QSize QCPLayoutGrid::minimumSizeHint() const
2954 QVector<int> minColWidths, minRowHeights;
2955 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2956 QSize result(0, 0);
2957 for (int i=0; i<minColWidths.size(); ++i)
2958 result.rwidth() += minColWidths.at(i);
2959 for (int i=0; i<minRowHeights.size(); ++i)
2960 result.rheight() += minRowHeights.at(i);
2961 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2962 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2963 return result;
2966 /* inherits documentation from base class */
2967 QSize QCPLayoutGrid::maximumSizeHint() const
2969 QVector<int> maxColWidths, maxRowHeights;
2970 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2972 QSize result(0, 0);
2973 for (int i=0; i<maxColWidths.size(); ++i)
2974 result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2975 for (int i=0; i<maxRowHeights.size(); ++i)
2976 result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2977 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2978 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2979 return result;
2982 /*! \internal
2984 Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights
2985 respectively.
2987 The minimum height of a row is the largest minimum height of any element in that row. The minimum
2988 width of a column is the largest minimum width of any element in that column.
2990 This is a helper function for \ref updateLayout.
2992 \see getMaximumRowColSizes
2994 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
2996 *minColWidths = QVector<int>(columnCount(), 0);
2997 *minRowHeights = QVector<int>(rowCount(), 0);
2998 for (int row=0; row<rowCount(); ++row)
3000 for (int col=0; col<columnCount(); ++col)
3002 if (mElements.at(row).at(col))
3004 QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
3005 QSize min = mElements.at(row).at(col)->minimumSize();
3006 QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3007 if (minColWidths->at(col) < final.width())
3008 (*minColWidths)[col] = final.width();
3009 if (minRowHeights->at(row) < final.height())
3010 (*minRowHeights)[row] = final.height();
3016 /*! \internal
3018 Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights
3019 respectively.
3021 The maximum height of a row is the smallest maximum height of any element in that row. The
3022 maximum width of a column is the smallest maximum width of any element in that column.
3024 This is a helper function for \ref updateLayout.
3026 \see getMinimumRowColSizes
3028 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3030 *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3031 *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3032 for (int row=0; row<rowCount(); ++row)
3034 for (int col=0; col<columnCount(); ++col)
3036 if (mElements.at(row).at(col))
3038 QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3039 QSize max = mElements.at(row).at(col)->maximumSize();
3040 QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3041 if (maxColWidths->at(col) > final.width())
3042 (*maxColWidths)[col] = final.width();
3043 if (maxRowHeights->at(row) > final.height())
3044 (*maxRowHeights)[row] = final.height();
3051 ////////////////////////////////////////////////////////////////////////////////////////////////////
3052 //////////////////// QCPLayoutInset
3053 ////////////////////////////////////////////////////////////////////////////////////////////////////
3054 /*! \class QCPLayoutInset
3055 \brief A layout that places child elements aligned to the border or arbitrarily positioned
3057 Elements are placed either aligned to the border or at arbitrary position in the area of the
3058 layout. Which placement applies is controlled with the \ref InsetPlacement (\ref
3059 setInsetPlacement).
3061 Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or
3062 addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset
3063 placement will default to \ref ipBorderAligned and the element will be aligned according to the
3064 \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at
3065 arbitrary position and size, defined by \a rect.
3067 The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively.
3069 This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout.
3072 /* start documentation of inline functions */
3074 /*! \fn virtual void QCPLayoutInset::simplify()
3076 The QCPInsetLayout does not need simplification since it can never have empty cells due to its
3077 linear index structure. This method does nothing.
3080 /* end documentation of inline functions */
3083 Creates an instance of QCPLayoutInset and sets default values.
3085 QCPLayoutInset::QCPLayoutInset()
3089 QCPLayoutInset::~QCPLayoutInset()
3091 // clear all child layout elements. This is important because only the specific layouts know how
3092 // to handle removing elements (clear calls virtual removeAt method to do that).
3093 clear();
3097 Returns the placement type of the element with the specified \a index.
3099 QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const
3101 if (elementAt(index))
3102 return mInsetPlacement.at(index);
3103 else
3105 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3106 return ipFree;
3111 Returns the alignment of the element with the specified \a index. The alignment only has a
3112 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned.
3114 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3116 if (elementAt(index))
3117 return mInsetAlignment.at(index);
3118 else
3120 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3121 return 0;
3126 Returns the rect of the element with the specified \a index. The rect only has a
3127 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
3129 QRectF QCPLayoutInset::insetRect(int index) const
3131 if (elementAt(index))
3132 return mInsetRect.at(index);
3133 else
3135 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3136 return QRectF();
3141 Sets the inset placement type of the element with the specified \a index to \a placement.
3143 \see InsetPlacement
3145 void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)
3147 if (elementAt(index))
3148 mInsetPlacement[index] = placement;
3149 else
3150 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3154 If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function
3155 is used to set the alignment of the element with the specified \a index to \a alignment.
3157 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3158 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3159 alignment flags will be ignored.
3161 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3163 if (elementAt(index))
3164 mInsetAlignment[index] = alignment;
3165 else
3166 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3170 If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the
3171 position and size of the element with the specified \a index to \a rect.
3173 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3174 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3175 corner of the layout, with 35% width and height of the parent layout.
3177 Note that the minimum and maximum sizes of the embedded element (\ref
3178 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced.
3180 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3182 if (elementAt(index))
3183 mInsetRect[index] = rect;
3184 else
3185 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3188 /* inherits documentation from base class */
3189 void QCPLayoutInset::updateLayout()
3191 for (int i=0; i<mElements.size(); ++i)
3193 QRect insetRect;
3194 QSize finalMinSize, finalMaxSize;
3195 QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3196 QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3197 finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3198 finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3199 finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3200 finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3201 if (mInsetPlacement.at(i) == ipFree)
3203 insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3204 rect().y()+rect().height()*mInsetRect.at(i).y(),
3205 rect().width()*mInsetRect.at(i).width(),
3206 rect().height()*mInsetRect.at(i).height());
3207 if (insetRect.size().width() < finalMinSize.width())
3208 insetRect.setWidth(finalMinSize.width());
3209 if (insetRect.size().height() < finalMinSize.height())
3210 insetRect.setHeight(finalMinSize.height());
3211 if (insetRect.size().width() > finalMaxSize.width())
3212 insetRect.setWidth(finalMaxSize.width());
3213 if (insetRect.size().height() > finalMaxSize.height())
3214 insetRect.setHeight(finalMaxSize.height());
3215 } else if (mInsetPlacement.at(i) == ipBorderAligned)
3217 insetRect.setSize(finalMinSize);
3218 Qt::Alignment al = mInsetAlignment.at(i);
3219 if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3220 else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3221 else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3222 if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3223 else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3224 else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3226 mElements.at(i)->setOuterRect(insetRect);
3230 /* inherits documentation from base class */
3231 int QCPLayoutInset::elementCount() const
3233 return mElements.size();
3236 /* inherits documentation from base class */
3237 QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
3239 if (index >= 0 && index < mElements.size())
3240 return mElements.at(index);
3241 else
3242 return 0;
3245 /* inherits documentation from base class */
3246 QCPLayoutElement *QCPLayoutInset::takeAt(int index)
3248 if (QCPLayoutElement *el = elementAt(index))
3250 releaseElement(el);
3251 mElements.removeAt(index);
3252 mInsetPlacement.removeAt(index);
3253 mInsetAlignment.removeAt(index);
3254 mInsetRect.removeAt(index);
3255 return el;
3256 } else
3258 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3259 return 0;
3263 /* inherits documentation from base class */
3264 bool QCPLayoutInset::take(QCPLayoutElement *element)
3266 if (element)
3268 for (int i=0; i<elementCount(); ++i)
3270 if (elementAt(i) == element)
3272 takeAt(i);
3273 return true;
3276 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3277 } else
3278 qDebug() << Q_FUNC_INFO << "Can't take null element";
3279 return false;
3283 The inset layout is sensitive to events only at areas where its (visible) child elements are
3284 sensitive. If the selectTest method of any of the child elements returns a positive number for \a
3285 pos, this method returns a value corresponding to 0.99 times the parent plot's selection
3286 tolerance. The inset layout is not selectable itself by default. So if \a onlySelectable is true,
3287 -1.0 is returned.
3289 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
3291 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3293 Q_UNUSED(details)
3294 if (onlySelectable)
3295 return -1;
3297 for (int i=0; i<mElements.size(); ++i)
3299 // inset layout shall only return positive selectTest, if actually an inset object is at pos
3300 // else it would block the entire underlying QCPAxisRect with its surface.
3301 if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3302 return mParentPlot->selectionTolerance()*0.99;
3304 return -1;
3308 Adds the specified \a element to the layout as an inset aligned at the border (\ref
3309 setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a
3310 alignment.
3312 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3313 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3314 alignment flags will be ignored.
3316 \see addElement(QCPLayoutElement *element, const QRectF &rect)
3318 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3320 if (element)
3322 if (element->layout()) // remove from old layout first
3323 element->layout()->take(element);
3324 mElements.append(element);
3325 mInsetPlacement.append(ipBorderAligned);
3326 mInsetAlignment.append(alignment);
3327 mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3328 adoptElement(element);
3329 } else
3330 qDebug() << Q_FUNC_INFO << "Can't add null element";
3334 Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref
3335 setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a
3336 rect.
3338 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3339 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3340 corner of the layout, with 35% width and height of the parent layout.
3342 \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3344 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3346 if (element)
3348 if (element->layout()) // remove from old layout first
3349 element->layout()->take(element);
3350 mElements.append(element);
3351 mInsetPlacement.append(ipFree);
3352 mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3353 mInsetRect.append(rect);
3354 adoptElement(element);
3355 } else
3356 qDebug() << Q_FUNC_INFO << "Can't add null element";
3360 ////////////////////////////////////////////////////////////////////////////////////////////////////
3361 //////////////////// QCPLineEnding
3362 ////////////////////////////////////////////////////////////////////////////////////////////////////
3364 /*! \class QCPLineEnding
3365 \brief Handles the different ending decorations for line-like items
3367 \image html QCPLineEnding.png "The various ending styles currently supported"
3369 For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
3370 has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
3372 The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
3373 be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
3374 the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
3375 For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
3376 directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
3377 respective arrow point inward.
3379 Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
3380 QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g.
3381 \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead
3385 Creates a QCPLineEnding instance with default values (style \ref esNone).
3387 QCPLineEnding::QCPLineEnding() :
3388 mStyle(esNone),
3389 mWidth(8),
3390 mLength(10),
3391 mInverted(false)
3396 Creates a QCPLineEnding instance with the specified values.
3398 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3399 mStyle(style),
3400 mWidth(width),
3401 mLength(length),
3402 mInverted(inverted)
3407 Sets the style of the ending decoration.
3409 void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
3411 mStyle = style;
3415 Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
3416 width defines the size perpendicular to the arrow's pointing direction.
3418 \see setLength
3420 void QCPLineEnding::setWidth(double width)
3422 mWidth = width;
3426 Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
3427 length defines the size in pointing direction.
3429 \see setWidth
3431 void QCPLineEnding::setLength(double length)
3433 mLength = length;
3437 Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point
3438 inward when \a inverted is set to true.
3440 Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or
3441 discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are
3442 affected by it, which can be used to control to which side the half bar points to.
3444 void QCPLineEnding::setInverted(bool inverted)
3446 mInverted = inverted;
3449 /*! \internal
3451 Returns the maximum pixel radius the ending decoration might cover, starting from the position
3452 the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
3454 This is relevant for clipping. Only omit painting of the decoration when the position where the
3455 decoration is supposed to be drawn is farther away from the clipping rect than the returned
3456 distance.
3458 double QCPLineEnding::boundingDistance() const
3460 switch (mStyle)
3462 case esNone:
3463 return 0;
3465 case esFlatArrow:
3466 case esSpikeArrow:
3467 case esLineArrow:
3468 case esSkewedBar:
3469 return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3471 case esDisc:
3472 case esSquare:
3473 case esDiamond:
3474 case esBar:
3475 case esHalfBar:
3476 return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3479 return 0;
3483 Starting from the origin of this line ending (which is style specific), returns the length
3484 covered by the line ending symbol, in backward direction.
3486 For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if
3487 both have the same \ref setLength value, because the spike arrow has an inward curved back, which
3488 reduces the length along its center axis (the drawing origin for arrows is at the tip).
3490 This function is used for precise, style specific placement of line endings, for example in
3491 QCPAxes.
3493 double QCPLineEnding::realLength() const
3495 switch (mStyle)
3497 case esNone:
3498 case esLineArrow:
3499 case esSkewedBar:
3500 case esBar:
3501 case esHalfBar:
3502 return 0;
3504 case esFlatArrow:
3505 return mLength;
3507 case esDisc:
3508 case esSquare:
3509 case esDiamond:
3510 return mWidth*0.5;
3512 case esSpikeArrow:
3513 return mLength*0.8;
3515 return 0;
3518 /*! \internal
3520 Draws the line ending with the specified \a painter at the position \a pos. The direction of the
3521 line ending is controlled with \a dir.
3523 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3525 if (mStyle == esNone)
3526 return;
3528 QVector2D lengthVec(dir.normalized());
3529 if (lengthVec.isNull())
3530 lengthVec = QVector2D(1, 0);
3531 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3532 lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3533 widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3535 QPen penBackup = painter->pen();
3536 QBrush brushBackup = painter->brush();
3537 QPen miterPen = penBackup;
3538 miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3539 QBrush brush(painter->pen().color(), Qt::SolidPattern);
3540 switch (mStyle)
3542 case esNone: break;
3543 case esFlatArrow:
3545 QPointF points[3] = {pos.toPointF(),
3546 (pos-lengthVec+widthVec).toPointF(),
3547 (pos-lengthVec-widthVec).toPointF()
3549 painter->setPen(miterPen);
3550 painter->setBrush(brush);
3551 painter->drawConvexPolygon(points, 3);
3552 painter->setBrush(brushBackup);
3553 painter->setPen(penBackup);
3554 break;
3556 case esSpikeArrow:
3558 QPointF points[4] = {pos.toPointF(),
3559 (pos-lengthVec+widthVec).toPointF(),
3560 (pos-lengthVec*0.8f).toPointF(),
3561 (pos-lengthVec-widthVec).toPointF()
3563 painter->setPen(miterPen);
3564 painter->setBrush(brush);
3565 painter->drawConvexPolygon(points, 4);
3566 painter->setBrush(brushBackup);
3567 painter->setPen(penBackup);
3568 break;
3570 case esLineArrow:
3572 QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3573 pos.toPointF(),
3574 (pos-lengthVec-widthVec).toPointF()
3576 painter->setPen(miterPen);
3577 painter->drawPolyline(points, 3);
3578 painter->setPen(penBackup);
3579 break;
3581 case esDisc:
3583 painter->setBrush(brush);
3584 painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3585 painter->setBrush(brushBackup);
3586 break;
3588 case esSquare:
3590 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3591 QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3592 (pos-widthVecPerp-widthVec).toPointF(),
3593 (pos+widthVecPerp-widthVec).toPointF(),
3594 (pos+widthVecPerp+widthVec).toPointF()
3596 painter->setPen(miterPen);
3597 painter->setBrush(brush);
3598 painter->drawConvexPolygon(points, 4);
3599 painter->setBrush(brushBackup);
3600 painter->setPen(penBackup);
3601 break;
3603 case esDiamond:
3605 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3606 QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3607 (pos-widthVec).toPointF(),
3608 (pos+widthVecPerp).toPointF(),
3609 (pos+widthVec).toPointF()
3611 painter->setPen(miterPen);
3612 painter->setBrush(brush);
3613 painter->drawConvexPolygon(points, 4);
3614 painter->setBrush(brushBackup);
3615 painter->setPen(penBackup);
3616 break;
3618 case esBar:
3620 painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3621 break;
3623 case esHalfBar:
3625 painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3626 break;
3628 case esSkewedBar:
3630 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3632 // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3633 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3634 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3635 } else
3637 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3638 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3639 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3641 break;
3646 /*! \internal
3647 \overload
3649 Draws the line ending. The direction is controlled with the \a angle parameter in radians.
3651 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3653 draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3657 ////////////////////////////////////////////////////////////////////////////////////////////////////
3658 //////////////////// QCPGrid
3659 ////////////////////////////////////////////////////////////////////////////////////////////////////
3661 /*! \class QCPGrid
3662 \brief Responsible for drawing the grid of a QCPAxis.
3664 This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the
3665 grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref
3666 QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself.
3668 The axis and grid drawing was split into two classes to allow them to be placed on different
3669 layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid
3670 in the background and the axes in the foreground, and any plottables/items in between. This
3671 described situation is the default setup, see the QCPLayer documentation.
3675 Creates a QCPGrid instance and sets default values.
3677 You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid.
3679 QCPGrid::QCPGrid(QCPAxis *parentAxis) :
3680 QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3681 mParentAxis(parentAxis)
3683 // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3684 setParent(parentAxis);
3685 setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3686 setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3687 setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3688 setSubGridVisible(false);
3689 setAntialiased(false);
3690 setAntialiasedSubGrid(false);
3691 setAntialiasedZeroLine(false);
3695 Sets whether grid lines at sub tick marks are drawn.
3697 \see setSubGridPen
3699 void QCPGrid::setSubGridVisible(bool visible)
3701 mSubGridVisible = visible;
3705 Sets whether sub grid lines are drawn antialiased.
3707 void QCPGrid::setAntialiasedSubGrid(bool enabled)
3709 mAntialiasedSubGrid = enabled;
3713 Sets whether zero lines are drawn antialiased.
3715 void QCPGrid::setAntialiasedZeroLine(bool enabled)
3717 mAntialiasedZeroLine = enabled;
3721 Sets the pen with which (major) grid lines are drawn.
3723 void QCPGrid::setPen(const QPen &pen)
3725 mPen = pen;
3729 Sets the pen with which sub grid lines are drawn.
3731 void QCPGrid::setSubGridPen(const QPen &pen)
3733 mSubGridPen = pen;
3737 Sets the pen with which zero lines are drawn.
3739 Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid
3740 lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
3742 void QCPGrid::setZeroLinePen(const QPen &pen)
3744 mZeroLinePen = pen;
3747 /*! \internal
3749 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
3750 before drawing the major grid lines.
3752 This is the antialiasing state the painter passed to the \ref draw method is in by default.
3754 This function takes into account the local setting of the antialiasing flag as well as the
3755 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
3756 QCustomPlot::setNotAntialiasedElements.
3758 \see setAntialiased
3760 void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
3762 applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
3765 /*! \internal
3767 Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
3768 over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
3770 void QCPGrid::draw(QCPPainter *painter)
3772 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3774 if (mSubGridVisible)
3775 drawSubGridLines(painter);
3776 drawGridLines(painter);
3779 /*! \internal
3781 Draws the main grid lines and possibly a zero line with the specified painter.
3783 This is a helper function called by \ref draw.
3785 void QCPGrid::drawGridLines(QCPPainter *painter) const
3787 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3789 int lowTick = mParentAxis->mLowestVisibleTick;
3790 int highTick = mParentAxis->mHighestVisibleTick;
3791 double t; // helper variable, result of coordinate-to-pixel transforms
3792 if (mParentAxis->orientation() == Qt::Horizontal)
3794 // draw zeroline:
3795 int zeroLineIndex = -1;
3796 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3798 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3799 painter->setPen(mZeroLinePen);
3800 double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3801 for (int i=lowTick; i <= highTick; ++i)
3803 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3805 zeroLineIndex = i;
3806 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3807 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3808 break;
3812 // draw grid lines:
3813 applyDefaultAntialiasingHint(painter);
3814 painter->setPen(mPen);
3815 for (int i=lowTick; i <= highTick; ++i)
3817 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3818 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3819 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3821 } else
3823 // draw zeroline:
3824 int zeroLineIndex = -1;
3825 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3827 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3828 painter->setPen(mZeroLinePen);
3829 double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3830 for (int i=lowTick; i <= highTick; ++i)
3832 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3834 zeroLineIndex = i;
3835 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3836 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3837 break;
3841 // draw grid lines:
3842 applyDefaultAntialiasingHint(painter);
3843 painter->setPen(mPen);
3844 for (int i=lowTick; i <= highTick; ++i)
3846 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3847 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3848 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3853 /*! \internal
3855 Draws the sub grid lines with the specified painter.
3857 This is a helper function called by \ref draw.
3859 void QCPGrid::drawSubGridLines(QCPPainter *painter) const
3861 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3863 applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
3864 double t; // helper variable, result of coordinate-to-pixel transforms
3865 painter->setPen(mSubGridPen);
3866 if (mParentAxis->orientation() == Qt::Horizontal)
3868 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3870 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
3871 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3873 } else
3875 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3877 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
3878 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3884 ////////////////////////////////////////////////////////////////////////////////////////////////////
3885 //////////////////// QCPAxis
3886 ////////////////////////////////////////////////////////////////////////////////////////////////////
3888 /*! \class QCPAxis
3889 \brief Manages a single axis inside a QCustomPlot.
3891 Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via
3892 QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
3893 QCustomPlot::yAxis2 (right).
3895 Axes are always part of an axis rect, see QCPAxisRect.
3896 \image html AxisNamesOverview.png
3897 <center>Naming convention of axis parts</center>
3900 \image html AxisRectSpacingOverview.png
3901 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line
3902 on the left represents the QCustomPlot widget border.</center>
3906 /* start of documentation of inline functions */
3908 /*! \fn Qt::Orientation QCPAxis::orientation() const
3910 Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
3911 from the axis type (left, top, right or bottom).
3913 \see orientation(AxisType type)
3916 /*! \fn QCPGrid *QCPAxis::grid() const
3918 Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the
3919 grid is displayed.
3922 /*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
3924 Returns the orientation of the specified axis type
3926 \see orientation()
3929 /* end of documentation of inline functions */
3930 /* start of documentation of signals */
3932 /*! \fn void QCPAxis::ticksRequest()
3934 This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
3935 labels for a replot.
3937 Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
3938 tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
3939 setTickVectorLabels.
3941 If you only want static ticks you probably don't need this signal, since you can just set the
3942 tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
3943 maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
3944 signal and set the vector/vectors there.
3947 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
3949 This signal is emitted when the range of this axis has changed. You can connect it to the \ref
3950 setRange slot of another axis to communicate the new range to the other axis, in order for it to
3951 be synchronized.
3953 You may also manipulate/correct the range with \ref setRange in a slot connected to this signal.
3954 This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper
3955 range shouldn't go beyond certain values. For example, the following slot would limit the x axis
3956 to only positive ranges:
3957 \code
3958 if (newRange.lower < 0)
3959 plot->xAxis->setRange(0, newRange.size());
3960 \endcode
3963 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
3964 \overload
3966 Additionally to the new range, this signal also provides the previous range held by the axis as
3967 \a oldRange.
3970 /*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
3972 This signal is emitted when the scale type changes, by calls to \ref setScaleType
3975 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
3977 This signal is emitted when the selection state of this axis has changed, either by user interaction
3978 or by a direct call to \ref setSelectedParts.
3981 /*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
3983 This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
3986 /* end of documentation of signals */
3989 Constructs an Axis instance of Type \a type for the axis rect \a parent.
3991 Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create
3992 them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,
3993 create them manually and then inject them also via \ref QCPAxisRect::addAxis.
3995 QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
3996 QCPLayerable(parent->parentPlot(), QString(), parent),
3997 // axis base:
3998 mAxisType(type),
3999 mAxisRect(parent),
4000 mPadding(5),
4001 mOrientation(orientation(type)),
4002 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4003 mSelectedParts(spNone),
4004 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4005 mSelectedBasePen(QPen(Qt::blue, 2)),
4006 // axis label:
4007 mLabel(),
4008 mLabelFont(mParentPlot->font()),
4009 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4010 mLabelColor(Qt::black),
4011 mSelectedLabelColor(Qt::blue),
4012 // tick labels:
4013 mTickLabels(true),
4014 mAutoTickLabels(true),
4015 mTickLabelType(ltNumber),
4016 mTickLabelFont(mParentPlot->font()),
4017 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4018 mTickLabelColor(Qt::black),
4019 mSelectedTickLabelColor(Qt::blue),
4020 mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4021 mDateTimeSpec(Qt::LocalTime),
4022 mNumberPrecision(6),
4023 mNumberFormatChar('g'),
4024 mNumberBeautifulPowers(true),
4025 // ticks and subticks:
4026 mTicks(true),
4027 mTickStep(1),
4028 mSubTickCount(4),
4029 mAutoTickCount(6),
4030 mAutoTicks(true),
4031 mAutoTickStep(true),
4032 mAutoSubTicks(true),
4033 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4034 mSelectedTickPen(QPen(Qt::blue, 2)),
4035 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4036 mSelectedSubTickPen(QPen(Qt::blue, 2)),
4037 // scale and range:
4038 mRange(0, 5),
4039 mRangeReversed(false),
4040 mScaleType(stLinear),
4041 mScaleLogBase(10),
4042 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4043 // internal members:
4044 mGrid(new QCPGrid(this)),
4045 mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4046 mLowestVisibleTick(0),
4047 mHighestVisibleTick(-1),
4048 mCachedMarginValid(false),
4049 mCachedMargin(0)
4051 mGrid->setVisible(false);
4052 setAntialiased(false);
4053 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
4055 if (type == atTop)
4057 setTickLabelPadding(3);
4058 setLabelPadding(6);
4059 } else if (type == atRight)
4061 setTickLabelPadding(7);
4062 setLabelPadding(12);
4063 } else if (type == atBottom)
4065 setTickLabelPadding(3);
4066 setLabelPadding(3);
4067 } else if (type == atLeft)
4069 setTickLabelPadding(5);
4070 setLabelPadding(10);
4074 QCPAxis::~QCPAxis()
4076 delete mAxisPainter;
4077 delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
4080 /* No documentation as it is a property getter */
4081 int QCPAxis::tickLabelPadding() const
4083 return mAxisPainter->tickLabelPadding;
4086 /* No documentation as it is a property getter */
4087 double QCPAxis::tickLabelRotation() const
4089 return mAxisPainter->tickLabelRotation;
4092 /* No documentation as it is a property getter */
4093 QCPAxis::LabelSide QCPAxis::tickLabelSide() const
4095 return mAxisPainter->tickLabelSide;
4098 /* No documentation as it is a property getter */
4099 QString QCPAxis::numberFormat() const
4101 QString result;
4102 result.append(mNumberFormatChar);
4103 if (mNumberBeautifulPowers)
4105 result.append(QLatin1Char('b'));
4106 if (mAxisPainter->numberMultiplyCross)
4107 result.append(QLatin1Char('c'));
4109 return result;
4112 /* No documentation as it is a property getter */
4113 int QCPAxis::tickLengthIn() const
4115 return mAxisPainter->tickLengthIn;
4118 /* No documentation as it is a property getter */
4119 int QCPAxis::tickLengthOut() const
4121 return mAxisPainter->tickLengthOut;
4124 /* No documentation as it is a property getter */
4125 int QCPAxis::subTickLengthIn() const
4127 return mAxisPainter->subTickLengthIn;
4130 /* No documentation as it is a property getter */
4131 int QCPAxis::subTickLengthOut() const
4133 return mAxisPainter->subTickLengthOut;
4136 /* No documentation as it is a property getter */
4137 int QCPAxis::labelPadding() const
4139 return mAxisPainter->labelPadding;
4142 /* No documentation as it is a property getter */
4143 int QCPAxis::offset() const
4145 return mAxisPainter->offset;
4148 /* No documentation as it is a property getter */
4149 QCPLineEnding QCPAxis::lowerEnding() const
4151 return mAxisPainter->lowerEnding;
4154 /* No documentation as it is a property getter */
4155 QCPLineEnding QCPAxis::upperEnding() const
4157 return mAxisPainter->upperEnding;
4161 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4162 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
4163 scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
4164 (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
4165 ticks, consider choosing a logarithm base of 100, 1000 or even higher.
4167 If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
4168 (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4169 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4170 part). To only display the decimal power, set the number precision to zero with
4171 \ref setNumberPrecision.
4173 void QCPAxis::setScaleType(QCPAxis::ScaleType type)
4175 if (mScaleType != type)
4177 mScaleType = type;
4178 if (mScaleType == stLogarithmic)
4179 setRange(mRange.sanitizedForLogScale());
4180 mCachedMarginValid = false;
4181 emit scaleTypeChanged(mScaleType);
4186 If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
4187 scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
4189 Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
4190 less major ticks, consider choosing \a base 100, 1000 or even higher.
4192 void QCPAxis::setScaleLogBase(double base)
4194 if (base > 1)
4196 mScaleLogBase = base;
4197 mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4198 mCachedMarginValid = false;
4199 } else
4200 qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4204 Sets the range of the axis.
4206 This slot may be connected with the \ref rangeChanged signal of another axis so this axis
4207 is always synchronized with the other axis range, when it changes.
4209 To invert the direction of an axis, use \ref setRangeReversed.
4211 void QCPAxis::setRange(const QCPRange &range)
4213 if (range.lower == mRange.lower && range.upper == mRange.upper)
4214 return;
4216 if (!QCPRange::validRange(range)) return;
4217 QCPRange oldRange = mRange;
4218 if (mScaleType == stLogarithmic)
4220 mRange = range.sanitizedForLogScale();
4221 } else
4223 mRange = range.sanitizedForLinScale();
4225 mCachedMarginValid = false;
4226 emit rangeChanged(mRange);
4227 emit rangeChanged(mRange, oldRange);
4231 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
4232 (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
4234 However, even when \a selectable is set to a value not allowing the selection of a specific part,
4235 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
4236 directly.
4238 \see SelectablePart, setSelectedParts
4240 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4242 if (mSelectableParts != selectable)
4244 mSelectableParts = selectable;
4245 emit selectableChanged(mSelectableParts);
4250 Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
4251 is selected, it uses a different pen/font.
4253 The entire selection mechanism for axes is handled automatically when \ref
4254 QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
4255 wish to change the selection state manually.
4257 This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
4259 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
4261 \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
4262 setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
4264 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4266 if (mSelectedParts != selected)
4268 mSelectedParts = selected;
4269 emit selectionChanged(mSelectedParts);
4274 \overload
4276 Sets the lower and upper bound of the axis range.
4278 To invert the direction of an axis, use \ref setRangeReversed.
4280 There is also a slot to set a range, see \ref setRange(const QCPRange &range).
4282 void QCPAxis::setRange(double lower, double upper)
4284 if (lower == mRange.lower && upper == mRange.upper)
4285 return;
4287 if (!QCPRange::validRange(lower, upper)) return;
4288 QCPRange oldRange = mRange;
4289 mRange.lower = lower;
4290 mRange.upper = upper;
4291 if (mScaleType == stLogarithmic)
4293 mRange = mRange.sanitizedForLogScale();
4294 } else
4296 mRange = mRange.sanitizedForLinScale();
4298 mCachedMarginValid = false;
4299 emit rangeChanged(mRange);
4300 emit rangeChanged(mRange, oldRange);
4304 \overload
4306 Sets the range of the axis.
4308 The \a position coordinate indicates together with the \a alignment parameter, where the new
4309 range will be positioned. \a size defines the size of the new axis range. \a alignment may be
4310 Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
4311 or center of the range to be aligned with \a position. Any other values of \a alignment will
4312 default to Qt::AlignCenter.
4314 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4316 if (alignment == Qt::AlignLeft)
4317 setRange(position, position+size);
4318 else if (alignment == Qt::AlignRight)
4319 setRange(position-size, position);
4320 else // alignment == Qt::AlignCenter
4321 setRange(position-size/2.0, position+size/2.0);
4325 Sets the lower bound of the axis range. The upper bound is not changed.
4326 \see setRange
4328 void QCPAxis::setRangeLower(double lower)
4330 if (mRange.lower == lower)
4331 return;
4333 QCPRange oldRange = mRange;
4334 mRange.lower = lower;
4335 if (mScaleType == stLogarithmic)
4337 mRange = mRange.sanitizedForLogScale();
4338 } else
4340 mRange = mRange.sanitizedForLinScale();
4342 mCachedMarginValid = false;
4343 emit rangeChanged(mRange);
4344 emit rangeChanged(mRange, oldRange);
4348 Sets the upper bound of the axis range. The lower bound is not changed.
4349 \see setRange
4351 void QCPAxis::setRangeUpper(double upper)
4353 if (mRange.upper == upper)
4354 return;
4356 QCPRange oldRange = mRange;
4357 mRange.upper = upper;
4358 if (mScaleType == stLogarithmic)
4360 mRange = mRange.sanitizedForLogScale();
4361 } else
4363 mRange = mRange.sanitizedForLinScale();
4365 mCachedMarginValid = false;
4366 emit rangeChanged(mRange);
4367 emit rangeChanged(mRange, oldRange);
4371 Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
4372 axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
4373 direction of increasing values is inverted.
4375 Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
4376 of the \ref setRange interface will still reference the mathematically smaller number than the \a
4377 upper part.
4379 void QCPAxis::setRangeReversed(bool reversed)
4381 if (mRangeReversed != reversed)
4383 mRangeReversed = reversed;
4384 mCachedMarginValid = false;
4389 Sets whether the tick positions should be calculated automatically (either from an automatically
4390 generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
4392 If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
4393 For these manual ticks you may let QCPAxis generate the appropriate labels automatically by
4394 leaving \ref setAutoTickLabels set to true. If you also wish to control the displayed labels
4395 manually, set \ref setAutoTickLabels to false and provide the label strings with \ref
4396 setTickVectorLabels.
4398 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4399 vectors in a slot connected to the \ref ticksRequest signal.
4401 \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep
4403 void QCPAxis::setAutoTicks(bool on)
4405 if (mAutoTicks != on)
4407 mAutoTicks = on;
4408 mCachedMarginValid = false;
4413 When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be
4414 generated in the visible range, approximately.
4416 It's not guaranteed that this number of ticks is met exactly, but approximately within a
4417 tolerance of about two.
4419 Only values greater than zero are accepted as \a approximateCount.
4421 \see setAutoTickStep, setAutoTicks, setAutoSubTicks
4423 void QCPAxis::setAutoTickCount(int approximateCount)
4425 if (mAutoTickCount != approximateCount)
4427 if (approximateCount > 0)
4429 mAutoTickCount = approximateCount;
4430 mCachedMarginValid = false;
4431 } else
4432 qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4437 Sets whether the tick labels are generated automatically. Depending on the tick label type (\ref
4438 ltNumber or \ref ltDateTime), the labels will either show the coordinate as floating point
4439 number (\ref setNumberFormat), or a date/time formatted according to \ref setDateTimeFormat.
4441 If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
4442 is usually used in a combination with \ref setAutoTicks set to false for complete control over
4443 tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
4444 etc. as tick labels.
4446 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4447 vectors in a slot connected to the \ref ticksRequest signal.
4449 \see setAutoTicks
4451 void QCPAxis::setAutoTickLabels(bool on)
4453 if (mAutoTickLabels != on)
4455 mAutoTickLabels = on;
4456 mCachedMarginValid = false;
4461 Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
4462 automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
4463 readable plots.
4465 The number of ticks the algorithm aims for within the visible range can be specified with \ref
4466 setAutoTickCount.
4468 If \a on is set to false, you may set the tick step manually with \ref setTickStep.
4470 \see setAutoTicks, setAutoSubTicks, setAutoTickCount
4472 void QCPAxis::setAutoTickStep(bool on)
4474 if (mAutoTickStep != on)
4476 mAutoTickStep = on;
4477 mCachedMarginValid = false;
4482 Sets whether the number of sub ticks in one tick interval is determined automatically. This
4483 works, as long as the tick step mantissa is a multiple of 0.5. When \ref setAutoTickStep is
4484 enabled, this is always the case.
4486 When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
4488 \see setAutoTickCount, setAutoTicks, setAutoTickStep
4490 void QCPAxis::setAutoSubTicks(bool on)
4492 if (mAutoSubTicks != on)
4494 mAutoSubTicks = on;
4495 mCachedMarginValid = false;
4500 Sets whether tick marks are displayed.
4502 Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
4503 that, see \ref setTickLabels.
4505 void QCPAxis::setTicks(bool show)
4507 if (mTicks != show)
4509 mTicks = show;
4510 mCachedMarginValid = false;
4515 Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
4517 void QCPAxis::setTickLabels(bool show)
4519 if (mTickLabels != show)
4521 mTickLabels = show;
4522 mCachedMarginValid = false;
4527 Sets the distance between the axis base line (including any outward ticks) and the tick labels.
4528 \see setLabelPadding, setPadding
4530 void QCPAxis::setTickLabelPadding(int padding)
4532 if (mAxisPainter->tickLabelPadding != padding)
4534 mAxisPainter->tickLabelPadding = padding;
4535 mCachedMarginValid = false;
4540 Sets whether the tick labels display numbers or dates/times.
4542 If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.
4544 If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.
4546 In QCustomPlot, date/time coordinates are <tt>double</tt> numbers representing the seconds since
4547 1970-01-01T00:00:00 UTC. This format can be retrieved from QDateTime objects with the
4548 QDateTime::toTime_t() function. Since this only gives a resolution of one second, there is also
4549 the QDateTime::toMSecsSinceEpoch() function which returns the timespan described above in
4550 milliseconds. Divide its return value by 1000.0 to get a value with the format needed for
4551 date/time plotting, with a resolution of one millisecond.
4553 Using the toMSecsSinceEpoch function allows dates that go back to 2nd January 4713 B.C.
4554 (represented by a negative number), unlike the toTime_t function, which works with unsigned
4555 integers and thus only goes back to 1st January 1970. So both for range and accuracy, use of
4556 toMSecsSinceEpoch()/1000.0 should be preferred as key coordinate for date/time axes.
4558 \see setTickLabels
4560 void QCPAxis::setTickLabelType(LabelType type)
4562 if (mTickLabelType != type)
4564 mTickLabelType = type;
4565 mCachedMarginValid = false;
4570 Sets the font of the tick labels.
4572 \see setTickLabels, setTickLabelColor
4574 void QCPAxis::setTickLabelFont(const QFont &font)
4576 if (font != mTickLabelFont)
4578 mTickLabelFont = font;
4579 mCachedMarginValid = false;
4584 Sets the color of the tick labels.
4586 \see setTickLabels, setTickLabelFont
4588 void QCPAxis::setTickLabelColor(const QColor &color)
4590 if (color != mTickLabelColor)
4592 mTickLabelColor = color;
4593 mCachedMarginValid = false;
4598 Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
4599 the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
4600 from -90 to 90 degrees.
4602 If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
4603 other angles, the label is drawn with an offset such that it seems to point toward or away from
4604 the tick mark.
4606 void QCPAxis::setTickLabelRotation(double degrees)
4608 if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4610 mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4611 mCachedMarginValid = false;
4616 Sets whether the tick labels (numbers) shall appear inside or outside the axis rect.
4618 The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels
4619 to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels
4620 appear on the inside are additionally clipped to the axis rect.
4622 void QCPAxis::setTickLabelSide(LabelSide side)
4624 mAxisPainter->tickLabelSide = side;
4625 mCachedMarginValid = false;
4629 Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
4630 for details about the \a format string, see the documentation of QDateTime::toString().
4632 Newlines can be inserted with "\n".
4634 \see setDateTimeSpec
4636 void QCPAxis::setDateTimeFormat(const QString &format)
4638 if (mDateTimeFormat != format)
4640 mDateTimeFormat = format;
4641 mCachedMarginValid = false;
4646 Sets the time spec that is used for the date time values when \ref setTickLabelType is \ref
4647 ltDateTime.
4649 The default value of QDateTime objects (and also QCustomPlot) is <tt>Qt::LocalTime</tt>. However,
4650 if the date time values passed to QCustomPlot are given in the UTC spec, set \a
4651 timeSpec to <tt>Qt::UTC</tt> to get the correct axis labels.
4653 \see setDateTimeFormat
4655 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4657 mDateTimeSpec = timeSpec;
4661 Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
4662 ltNumber). This \a formatCode is an extended version of the format code used e.g. by
4663 QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
4664 section in the detailed description of the QString class. \a formatCode is a string of one, two
4665 or three characters. The first character is identical to the normal format code used by Qt. In
4666 short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
4667 whichever is shorter.
4669 The second and third characters are optional and specific to QCustomPlot:\n
4670 If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
4671 "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
4672 "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
4673 [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
4674 If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
4675 be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
4676 cross and 183 (0xB7) for the dot.
4678 If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
4679 option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4680 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4681 part). To only display the decimal power, set the number precision to zero with \ref
4682 setNumberPrecision.
4684 Examples for \a formatCode:
4685 \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
4686 normal scientific format is used
4687 \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
4688 beautifully typeset decimal powers and a dot as multiplication sign
4689 \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
4690 multiplication sign
4691 \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
4692 powers. Format code will be reduced to 'f'.
4693 \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
4694 code will not be changed.
4696 void QCPAxis::setNumberFormat(const QString &formatCode)
4698 if (formatCode.isEmpty())
4700 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4701 return;
4703 mCachedMarginValid = false;
4705 // interpret first char as number format char:
4706 QString allowedFormatChars(QLatin1String("eEfgG"));
4707 if (allowedFormatChars.contains(formatCode.at(0)))
4709 mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4710 } else
4712 qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4713 return;
4715 if (formatCode.length() < 2)
4717 mNumberBeautifulPowers = false;
4718 mAxisPainter->numberMultiplyCross = false;
4719 return;
4722 // interpret second char as indicator for beautiful decimal powers:
4723 if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4725 mNumberBeautifulPowers = true;
4726 } else
4728 qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4729 return;
4731 if (formatCode.length() < 3)
4733 mAxisPainter->numberMultiplyCross = false;
4734 return;
4737 // interpret third char as indicator for dot or cross multiplication symbol:
4738 if (formatCode.at(2) == QLatin1Char('c'))
4740 mAxisPainter->numberMultiplyCross = true;
4741 } else if (formatCode.at(2) == QLatin1Char('d'))
4743 mAxisPainter->numberMultiplyCross = false;
4744 } else
4746 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4747 return;
4752 Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
4753 for details. The effect of precisions are most notably for number Formats starting with 'e', see
4754 \ref setNumberFormat
4756 If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
4757 setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
4758 usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
4759 scaling (the redundant "1 [multiplication sign]" part). To only display the decimal power "10
4760 [superscript] n", set \a precision to zero.
4762 void QCPAxis::setNumberPrecision(int precision)
4764 if (mNumberPrecision != precision)
4766 mNumberPrecision = precision;
4767 mCachedMarginValid = false;
4772 If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
4773 The tick step is the interval between (major) ticks, in plot coordinates.
4774 \see setSubTickCount
4776 void QCPAxis::setTickStep(double step)
4778 if (mTickStep != step)
4780 mTickStep = step;
4781 mCachedMarginValid = false;
4786 If you want full control over what ticks (and possibly labels) the axes show, this function is
4787 used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
4788 the provided tick vector will be overwritten with automatically generated tick coordinates upon
4789 replot. The labels of the ticks can be generated automatically when \ref setAutoTickLabels is
4790 left enabled. If it is disabled, you can set the labels manually with \ref setTickVectorLabels.
4792 \a vec is a vector containing the positions of the ticks, in plot coordinates.
4794 \warning \a vec must be sorted in ascending order, no additional checks are made to ensure this.
4796 \see setTickVectorLabels
4798 void QCPAxis::setTickVector(const QVector<double> &vec)
4800 // don't check whether mTickVector != vec here, because it takes longer than we would save
4801 mTickVector = vec;
4802 mCachedMarginValid = false;
4806 If you want full control over what ticks and labels the axes show, this function is used to set a
4807 number of QStrings that will be displayed at the tick positions which you need to provide with
4808 \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
4809 \ref setAutoTicks and \ref setAutoTickLabels first.)
4811 \a vec is a vector containing the labels of the ticks. The entries correspond to the respective
4812 indices in the tick vector, passed via \ref setTickVector.
4814 \see setTickVector
4816 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4818 // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4819 mTickVectorLabels = vec;
4820 mCachedMarginValid = false;
4824 Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
4825 plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
4826 zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4827 they won't collide with the ticks.
4829 \see setSubTickLength, setTickLengthIn, setTickLengthOut
4831 void QCPAxis::setTickLength(int inside, int outside)
4833 setTickLengthIn(inside);
4834 setTickLengthOut(outside);
4838 Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4839 inside the plot.
4841 \see setTickLengthOut, setTickLength, setSubTickLength
4843 void QCPAxis::setTickLengthIn(int inside)
4845 if (mAxisPainter->tickLengthIn != inside)
4847 mAxisPainter->tickLengthIn = inside;
4852 Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
4853 outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4854 increase their distance to the axis accordingly, so they won't collide with the ticks.
4856 \see setTickLengthIn, setTickLength, setSubTickLength
4858 void QCPAxis::setTickLengthOut(int outside)
4860 if (mAxisPainter->tickLengthOut != outside)
4862 mAxisPainter->tickLengthOut = outside;
4863 mCachedMarginValid = false; // only outside tick length can change margin
4868 Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
4869 divides the tick intervals in four sub intervals.
4871 By default, the number of sub ticks is chosen automatically in a reasonable manner as long as the
4872 mantissa of the tick step is a multiple of 0.5. When \ref setAutoTickStep is enabled, this is
4873 always the case.
4875 If you want to disable automatic sub tick count and use this function to set the count manually,
4876 see \ref setAutoSubTicks.
4878 void QCPAxis::setSubTickCount(int count)
4880 mSubTickCount = count;
4884 Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
4885 the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4886 than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4887 so they won't collide with the ticks.
4889 \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4891 void QCPAxis::setSubTickLength(int inside, int outside)
4893 setSubTickLengthIn(inside);
4894 setSubTickLengthOut(outside);
4898 Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4899 the plot.
4901 \see setSubTickLengthOut, setSubTickLength, setTickLength
4903 void QCPAxis::setSubTickLengthIn(int inside)
4905 if (mAxisPainter->subTickLengthIn != inside)
4907 mAxisPainter->subTickLengthIn = inside;
4912 Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
4913 outside the plot. If \a outside is greater than zero, the tick labels will increase their
4914 distance to the axis accordingly, so they won't collide with the ticks.
4916 \see setSubTickLengthIn, setSubTickLength, setTickLength
4918 void QCPAxis::setSubTickLengthOut(int outside)
4920 if (mAxisPainter->subTickLengthOut != outside)
4922 mAxisPainter->subTickLengthOut = outside;
4923 mCachedMarginValid = false; // only outside tick length can change margin
4928 Sets the pen, the axis base line is drawn with.
4930 \see setTickPen, setSubTickPen
4932 void QCPAxis::setBasePen(const QPen &pen)
4934 mBasePen = pen;
4938 Sets the pen, tick marks will be drawn with.
4940 \see setTickLength, setBasePen
4942 void QCPAxis::setTickPen(const QPen &pen)
4944 mTickPen = pen;
4948 Sets the pen, subtick marks will be drawn with.
4950 \see setSubTickCount, setSubTickLength, setBasePen
4952 void QCPAxis::setSubTickPen(const QPen &pen)
4954 mSubTickPen = pen;
4958 Sets the font of the axis label.
4960 \see setLabelColor
4962 void QCPAxis::setLabelFont(const QFont &font)
4964 if (mLabelFont != font)
4966 mLabelFont = font;
4967 mCachedMarginValid = false;
4972 Sets the color of the axis label.
4974 \see setLabelFont
4976 void QCPAxis::setLabelColor(const QColor &color)
4978 mLabelColor = color;
4982 Sets the text of the axis label that will be shown below/above or next to the axis, depending on
4983 its orientation. To disable axis labels, pass an empty string as \a str.
4985 void QCPAxis::setLabel(const QString &str)
4987 if (mLabel != str)
4989 mLabel = str;
4990 mCachedMarginValid = false;
4995 Sets the distance between the tick labels and the axis label.
4997 \see setTickLabelPadding, setPadding
4999 void QCPAxis::setLabelPadding(int padding)
5001 if (mAxisPainter->labelPadding != padding)
5003 mAxisPainter->labelPadding = padding;
5004 mCachedMarginValid = false;
5009 Sets the padding of the axis.
5011 When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space,
5012 that is left blank.
5014 The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled.
5016 \see setLabelPadding, setTickLabelPadding
5018 void QCPAxis::setPadding(int padding)
5020 if (mPadding != padding)
5022 mPadding = padding;
5023 mCachedMarginValid = false;
5028 Sets the offset the axis has to its axis rect side.
5030 If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
5031 only the offset of the inner most axis has meaning (even if it is set to be invisible). The
5032 offset of the other, outer axes is controlled automatically, to place them at appropriate
5033 positions.
5035 void QCPAxis::setOffset(int offset)
5037 mAxisPainter->offset = offset;
5041 Sets the font that is used for tick labels when they are selected.
5043 \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5045 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5047 if (font != mSelectedTickLabelFont)
5049 mSelectedTickLabelFont = font;
5050 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5055 Sets the font that is used for the axis label when it is selected.
5057 \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5059 void QCPAxis::setSelectedLabelFont(const QFont &font)
5061 mSelectedLabelFont = font;
5062 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5066 Sets the color that is used for tick labels when they are selected.
5068 \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5070 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5072 if (color != mSelectedTickLabelColor)
5074 mSelectedTickLabelColor = color;
5079 Sets the color that is used for the axis label when it is selected.
5081 \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5083 void QCPAxis::setSelectedLabelColor(const QColor &color)
5085 mSelectedLabelColor = color;
5089 Sets the pen that is used to draw the axis base line when selected.
5091 \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5093 void QCPAxis::setSelectedBasePen(const QPen &pen)
5095 mSelectedBasePen = pen;
5099 Sets the pen that is used to draw the (major) ticks when selected.
5101 \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5103 void QCPAxis::setSelectedTickPen(const QPen &pen)
5105 mSelectedTickPen = pen;
5109 Sets the pen that is used to draw the subticks when selected.
5111 \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5113 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5115 mSelectedSubTickPen = pen;
5119 Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available
5120 styles.
5122 For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending.
5123 Note that this meaning does not change when the axis range is reversed with \ref
5124 setRangeReversed.
5126 \see setUpperEnding
5128 void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
5130 mAxisPainter->lowerEnding = ending;
5134 Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available
5135 styles.
5137 For horizontal axes, this method refers to the right ending, for vertical axes the top ending.
5138 Note that this meaning does not change when the axis range is reversed with \ref
5139 setRangeReversed.
5141 \see setLowerEnding
5143 void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
5145 mAxisPainter->upperEnding = ending;
5149 If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
5150 bounds of the range. The range is simply moved by \a diff.
5152 If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
5153 corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
5155 void QCPAxis::moveRange(double diff)
5157 QCPRange oldRange = mRange;
5158 if (mScaleType == stLinear)
5160 mRange.lower += diff;
5161 mRange.upper += diff;
5162 } else // mScaleType == stLogarithmic
5164 mRange.lower *= diff;
5165 mRange.upper *= diff;
5167 mCachedMarginValid = false;
5168 emit rangeChanged(mRange);
5169 emit rangeChanged(mRange, oldRange);
5173 Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
5174 factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
5175 coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
5176 around 1.0 will have moved symmetrically closer to 1.0).
5178 void QCPAxis::scaleRange(double factor, double center)
5180 QCPRange oldRange = mRange;
5181 if (mScaleType == stLinear)
5183 QCPRange newRange;
5184 newRange.lower = (mRange.lower-center)*factor + center;
5185 newRange.upper = (mRange.upper-center)*factor + center;
5186 if (QCPRange::validRange(newRange))
5187 mRange = newRange.sanitizedForLinScale();
5188 } else // mScaleType == stLogarithmic
5190 if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5192 QCPRange newRange;
5193 newRange.lower = qPow(mRange.lower/center, factor)*center;
5194 newRange.upper = qPow(mRange.upper/center, factor)*center;
5195 if (QCPRange::validRange(newRange))
5196 mRange = newRange.sanitizedForLogScale();
5197 } else
5198 qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5200 mCachedMarginValid = false;
5201 emit rangeChanged(mRange);
5202 emit rangeChanged(mRange, oldRange);
5206 Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will
5207 be done around the center of the current axis range.
5209 For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs
5210 plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the
5211 axis rect has.
5213 This is an operation that changes the range of this axis once, it doesn't fix the scale ratio
5214 indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent
5215 won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent
5216 will follow.
5218 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5220 int otherPixelSize, ownPixelSize;
5222 if (otherAxis->orientation() == Qt::Horizontal)
5223 otherPixelSize = otherAxis->axisRect()->width();
5224 else
5225 otherPixelSize = otherAxis->axisRect()->height();
5227 if (orientation() == Qt::Horizontal)
5228 ownPixelSize = axisRect()->width();
5229 else
5230 ownPixelSize = axisRect()->height();
5232 double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5233 setRange(range().center(), newRangeSize, Qt::AlignCenter);
5237 Changes the axis range such that all plottables associated with this axis are fully visible in
5238 that dimension.
5240 \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
5242 void QCPAxis::rescale(bool onlyVisiblePlottables)
5244 QList<QCPAbstractPlottable*> p = plottables();
5245 QCPRange newRange;
5246 bool haveRange = false;
5247 for (int i=0; i<p.size(); ++i)
5249 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5250 continue;
5251 QCPRange plottableRange;
5252 bool currentFoundRange;
5253 QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5254 if (mScaleType == stLogarithmic)
5255 signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5256 if (p.at(i)->keyAxis() == this)
5257 plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5258 else
5259 plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5260 if (currentFoundRange)
5262 if (!haveRange)
5263 newRange = plottableRange;
5264 else
5265 newRange.expand(plottableRange);
5266 haveRange = true;
5269 if (haveRange)
5271 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
5273 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
5274 if (mScaleType == stLinear)
5276 newRange.lower = center-mRange.size()/2.0;
5277 newRange.upper = center+mRange.size()/2.0;
5278 } else // mScaleType == stLogarithmic
5280 newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5281 newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5284 setRange(newRange);
5289 Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
5291 double QCPAxis::pixelToCoord(double value) const
5293 if (orientation() == Qt::Horizontal)
5295 if (mScaleType == stLinear)
5297 if (!mRangeReversed)
5298 return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5299 else
5300 return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5301 } else // mScaleType == stLogarithmic
5303 if (!mRangeReversed)
5304 return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5305 else
5306 return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5308 } else // orientation() == Qt::Vertical
5310 if (mScaleType == stLinear)
5312 if (!mRangeReversed)
5313 return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5314 else
5315 return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5316 } else // mScaleType == stLogarithmic
5318 if (!mRangeReversed)
5319 return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5320 else
5321 return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5327 Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
5329 double QCPAxis::coordToPixel(double value) const
5331 if (orientation() == Qt::Horizontal)
5333 if (mScaleType == stLinear)
5335 if (!mRangeReversed)
5336 return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5337 else
5338 return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5339 } else // mScaleType == stLogarithmic
5341 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5342 return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5343 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5344 return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5345 else
5347 if (!mRangeReversed)
5348 return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5349 else
5350 return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5353 } else // orientation() == Qt::Vertical
5355 if (mScaleType == stLinear)
5357 if (!mRangeReversed)
5358 return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5359 else
5360 return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5361 } else // mScaleType == stLogarithmic
5363 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5364 return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5365 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5366 return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5367 else
5369 if (!mRangeReversed)
5370 return mAxisRect->bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5371 else
5372 return mAxisRect->bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5379 Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
5380 is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
5381 function does not change the current selection state of the axis.
5383 If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
5385 \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
5387 QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const
5389 if (!mVisible)
5390 return spNone;
5392 if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5393 return spAxis;
5394 else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5395 return spTickLabels;
5396 else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5397 return spAxisLabel;
5398 else
5399 return spNone;
5402 /* inherits documentation from base class */
5403 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5405 if (!mParentPlot) return -1;
5406 SelectablePart part = getPartAt(pos);
5407 if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5408 return -1;
5410 if (details)
5411 details->setValue(part);
5412 return mParentPlot->selectionTolerance()*0.99;
5416 Returns a list of all the plottables that have this axis as key or value axis.
5418 If you are only interested in plottables of type QCPGraph, see \ref graphs.
5420 \see graphs, items
5422 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5424 QList<QCPAbstractPlottable*> result;
5425 if (!mParentPlot) return result;
5427 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5429 if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5430 result.append(mParentPlot->mPlottables.at(i));
5432 return result;
5436 Returns a list of all the graphs that have this axis as key or value axis.
5438 \see plottables, items
5440 QList<QCPGraph*> QCPAxis::graphs() const
5442 QList<QCPGraph*> result;
5443 if (!mParentPlot) return result;
5445 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5447 if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5448 result.append(mParentPlot->mGraphs.at(i));
5450 return result;
5454 Returns a list of all the items that are associated with this axis. An item is considered
5455 associated with an axis if at least one of its positions uses the axis as key or value axis.
5457 \see plottables, graphs
5459 QList<QCPAbstractItem*> QCPAxis::items() const
5461 QList<QCPAbstractItem*> result;
5462 if (!mParentPlot) return result;
5464 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5466 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5467 for (int posId=0; posId<positions.size(); ++posId)
5469 if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5471 result.append(mParentPlot->mItems.at(itemId));
5472 break;
5476 return result;
5480 Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to
5481 QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)
5483 QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
5485 switch (side)
5487 case QCP::msLeft: return atLeft;
5488 case QCP::msRight: return atRight;
5489 case QCP::msTop: return atTop;
5490 case QCP::msBottom: return atBottom;
5491 default: break;
5493 qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5494 return atLeft;
5498 Returns the axis type that describes the opposite axis of an axis with the specified \a type.
5500 QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
5502 switch (type)
5504 case atLeft: return atRight; break;
5505 case atRight: return atLeft; break;
5506 case atBottom: return atTop; break;
5507 case atTop: return atBottom; break;
5508 default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5512 /*! \internal
5514 This function is called to prepare the tick vector, sub tick vector and tick label vector. If
5515 \ref setAutoTicks is set to true, appropriate tick values are determined automatically via \ref
5516 generateAutoTicks. If it's set to false, the signal ticksRequest is emitted, which can be used to
5517 provide external tick positions. Then the sub tick vectors and tick label vectors are created.
5519 void QCPAxis::setupTickVectors()
5521 if (!mParentPlot) return;
5522 if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5524 // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5525 if (mAutoTicks)
5527 generateAutoTicks();
5528 } else
5530 emit ticksRequest();
5533 visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
5534 if (mTickVector.isEmpty())
5536 mSubTickVector.clear();
5537 return;
5540 // generate subticks between ticks:
5541 mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5542 if (mSubTickCount > 0)
5544 double subTickStep = 0;
5545 double subTickPosition = 0;
5546 int subTickIndex = 0;
5547 bool done = false;
5548 int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick-1 : mLowestVisibleTick;
5549 int highTick = mHighestVisibleTick < mTickVector.size()-1 ? mHighestVisibleTick+1 : mHighestVisibleTick;
5550 for (int i=lowTick+1; i<=highTick; ++i)
5552 subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5553 for (int k=1; k<=mSubTickCount; ++k)
5555 subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5556 if (subTickPosition < mRange.lower)
5557 continue;
5558 if (subTickPosition > mRange.upper)
5560 done = true;
5561 break;
5563 mSubTickVector[subTickIndex] = subTickPosition;
5564 subTickIndex++;
5566 if (done) break;
5568 mSubTickVector.resize(subTickIndex);
5571 // generate tick labels according to tick positions:
5572 if (mAutoTickLabels)
5574 int vecsize = mTickVector.size();
5575 mTickVectorLabels.resize(vecsize);
5576 if (mTickLabelType == ltNumber)
5578 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5579 mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5580 } else if (mTickLabelType == ltDateTime)
5582 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5584 #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")
5585 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5586 #else
5587 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5588 #endif
5591 } else // mAutoTickLabels == false
5593 if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5595 emit ticksRequest();
5597 // make sure provided tick label vector has correct (minimal) length:
5598 if (mTickVectorLabels.size() < mTickVector.size())
5599 mTickVectorLabels.resize(mTickVector.size());
5603 /*! \internal
5605 If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
5606 generate reasonable tick positions (and subtick count). The algorithm tries to create
5607 approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount).
5609 If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is generated at every
5610 power of the current logarithm base, set via \ref setScaleLogBase.
5612 void QCPAxis::generateAutoTicks()
5614 if (mScaleType == stLinear)
5616 if (mAutoTickStep)
5618 // Generate tick positions according to linear scaling:
5619 mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5620 double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5621 double tickStepMantissa = mTickStep/magnitudeFactor;
5622 if (tickStepMantissa < 5)
5624 // round digit after decimal point to 0.5
5625 mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5626 } else
5628 // round to first digit in multiples of 2
5629 mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5632 if (mAutoSubTicks)
5633 mSubTickCount = calculateAutoSubTickCount(mTickStep);
5634 // Generate tick positions according to mTickStep:
5635 qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5636 qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5637 int tickcount = lastStep-firstStep+1;
5638 if (tickcount < 0) tickcount = 0;
5639 mTickVector.resize(tickcount);
5640 for (int i=0; i<tickcount; ++i)
5641 mTickVector[i] = (firstStep+i)*mTickStep;
5642 } else // mScaleType == stLogarithmic
5644 // Generate tick positions according to logbase scaling:
5645 if (mRange.lower > 0 && mRange.upper > 0) // positive range
5647 double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5648 double currentMag = lowerMag;
5649 mTickVector.clear();
5650 mTickVector.append(currentMag);
5651 while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5653 currentMag *= mScaleLogBase;
5654 mTickVector.append(currentMag);
5656 } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5658 double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5659 double currentMag = lowerMag;
5660 mTickVector.clear();
5661 mTickVector.append(currentMag);
5662 while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5664 currentMag /= mScaleLogBase;
5665 mTickVector.append(currentMag);
5667 } else // invalid range for logarithmic scale, because lower and upper have different sign
5669 mTickVector.clear();
5670 qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5675 /*! \internal
5677 Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
5678 tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
5679 Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
5680 because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667. Note
5681 that a subtick count of 4 means dividing the major tick step into 5 sections.
5683 This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
5684 with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
5685 fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
5686 returned.
5688 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5690 int result = mSubTickCount; // default to current setting, if no proper value can be found
5692 // get mantissa of tickstep:
5693 double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5694 double tickStepMantissa = tickStep/magnitudeFactor;
5696 // separate integer and fractional part of mantissa:
5697 double epsilon = 0.01;
5698 double intPartf;
5699 int intPart;
5700 double fracPart = modf(tickStepMantissa, &intPartf);
5701 intPart = intPartf;
5703 // handle cases with (almost) integer mantissa:
5704 if (fracPart < epsilon || 1.0-fracPart < epsilon)
5706 if (1.0-fracPart < epsilon)
5707 ++intPart;
5708 switch (intPart)
5710 case 1: result = 4; break; // 1.0 -> 0.2 substep
5711 case 2: result = 3; break; // 2.0 -> 0.5 substep
5712 case 3: result = 2; break; // 3.0 -> 1.0 substep
5713 case 4: result = 3; break; // 4.0 -> 1.0 substep
5714 case 5: result = 4; break; // 5.0 -> 1.0 substep
5715 case 6: result = 2; break; // 6.0 -> 2.0 substep
5716 case 7: result = 6; break; // 7.0 -> 1.0 substep
5717 case 8: result = 3; break; // 8.0 -> 2.0 substep
5718 case 9: result = 2; break; // 9.0 -> 3.0 substep
5720 } else
5722 // handle cases with significantly fractional mantissa:
5723 if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5725 switch (intPart)
5727 case 1: result = 2; break; // 1.5 -> 0.5 substep
5728 case 2: result = 4; break; // 2.5 -> 0.5 substep
5729 case 3: result = 4; break; // 3.5 -> 0.7 substep
5730 case 4: result = 2; break; // 4.5 -> 1.5 substep
5731 case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5732 case 6: result = 4; break; // 6.5 -> 1.3 substep
5733 case 7: result = 2; break; // 7.5 -> 2.5 substep
5734 case 8: result = 4; break; // 8.5 -> 1.7 substep
5735 case 9: result = 4; break; // 9.5 -> 1.9 substep
5738 // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5741 return result;
5744 /* inherits documentation from base class */
5745 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5747 Q_UNUSED(event)
5748 SelectablePart part = details.value<SelectablePart>();
5749 if (mSelectableParts.testFlag(part))
5751 SelectableParts selBefore = mSelectedParts;
5752 setSelectedParts(additive ? mSelectedParts^part : part);
5753 if (selectionStateChanged)
5754 *selectionStateChanged = mSelectedParts != selBefore;
5758 /* inherits documentation from base class */
5759 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5761 SelectableParts selBefore = mSelectedParts;
5762 setSelectedParts(mSelectedParts & ~mSelectableParts);
5763 if (selectionStateChanged)
5764 *selectionStateChanged = mSelectedParts != selBefore;
5767 /*! \internal
5769 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
5770 before drawing axis lines.
5772 This is the antialiasing state the painter passed to the \ref draw method is in by default.
5774 This function takes into account the local setting of the antialiasing flag as well as the
5775 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
5776 QCustomPlot::setNotAntialiasedElements.
5778 \see setAntialiased
5780 void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
5782 applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
5785 /*! \internal
5787 Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
5790 void QCPAxis::draw(QCPPainter *painter)
5792 const int lowTick = mLowestVisibleTick;
5793 const int highTick = mHighestVisibleTick;
5794 QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5795 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5796 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5797 tickPositions.reserve(highTick-lowTick+1);
5798 tickLabels.reserve(highTick-lowTick+1);
5799 subTickPositions.reserve(mSubTickVector.size());
5801 if (mTicks)
5803 for (int i=lowTick; i<=highTick; ++i)
5805 tickPositions.append(coordToPixel(mTickVector.at(i)));
5806 if (mTickLabels)
5807 tickLabels.append(mTickVectorLabels.at(i));
5810 if (mSubTickCount > 0)
5812 const int subTickCount = mSubTickVector.size();
5813 for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5814 subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5817 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5818 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5819 mAxisPainter->type = mAxisType;
5820 mAxisPainter->basePen = getBasePen();
5821 mAxisPainter->labelFont = getLabelFont();
5822 mAxisPainter->labelColor = getLabelColor();
5823 mAxisPainter->label = mLabel;
5824 mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber;
5825 mAxisPainter->tickPen = getTickPen();
5826 mAxisPainter->subTickPen = getSubTickPen();
5827 mAxisPainter->tickLabelFont = getTickLabelFont();
5828 mAxisPainter->tickLabelColor = getTickLabelColor();
5829 mAxisPainter->axisRect = mAxisRect->rect();
5830 mAxisPainter->viewportRect = mParentPlot->viewport();
5831 mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5832 mAxisPainter->reversedEndings = mRangeReversed;
5833 mAxisPainter->tickPositions = tickPositions;
5834 mAxisPainter->tickLabels = tickLabels;
5835 mAxisPainter->subTickPositions = subTickPositions;
5836 mAxisPainter->draw(painter);
5839 /*! \internal
5841 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
5842 the current range. The return values are indices of the tick vector, not the positions of the
5843 ticks themselves.
5845 The actual use of this function is when an external tick vector is provided, since it might
5846 exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
5847 subticks.
5849 If all ticks are outside the axis range, an inverted range is returned, i.e. highIndex will be
5850 smaller than lowIndex. There is one case, where this function returns indices that are not really
5851 visible in the current axis range: When the tick spacing is larger than the axis range size and
5852 one tick is below the axis range and the next tick is already above the axis range. Because in
5853 such cases it is usually desirable to know the tick pair, to draw proper subticks.
5855 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5857 bool lowFound = false;
5858 bool highFound = false;
5859 lowIndex = 0;
5860 highIndex = -1;
5862 for (int i=0; i < mTickVector.size(); ++i)
5864 if (mTickVector.at(i) >= mRange.lower)
5866 lowFound = true;
5867 lowIndex = i;
5868 break;
5871 for (int i=mTickVector.size()-1; i >= 0; --i)
5873 if (mTickVector.at(i) <= mRange.upper)
5875 highFound = true;
5876 highIndex = i;
5877 break;
5881 if (!lowFound && highFound)
5882 lowIndex = highIndex+1;
5883 else if (lowFound && !highFound)
5884 highIndex = lowIndex-1;
5887 /*! \internal
5889 A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
5890 scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
5891 This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
5893 \see basePow, setScaleLogBase, setScaleType
5895 double QCPAxis::baseLog(double value) const
5897 return qLn(value)*mScaleLogBaseLogInv;
5900 /*! \internal
5902 A power function with the base mScaleLogBase, used mostly for coordinate transforms in
5903 logarithmic scales with arbitrary log base.
5905 \see baseLog, setScaleLogBase, setScaleType
5907 double QCPAxis::basePow(double value) const
5909 return qPow(mScaleLogBase, value);
5912 /*! \internal
5914 Returns the pen that is used to draw the axis base line. Depending on the selection state, this
5915 is either mSelectedBasePen or mBasePen.
5917 QPen QCPAxis::getBasePen() const
5919 return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5922 /*! \internal
5924 Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
5925 is either mSelectedTickPen or mTickPen.
5927 QPen QCPAxis::getTickPen() const
5929 return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5932 /*! \internal
5934 Returns the pen that is used to draw the subticks. Depending on the selection state, this
5935 is either mSelectedSubTickPen or mSubTickPen.
5937 QPen QCPAxis::getSubTickPen() const
5939 return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
5942 /*! \internal
5944 Returns the font that is used to draw the tick labels. Depending on the selection state, this
5945 is either mSelectedTickLabelFont or mTickLabelFont.
5947 QFont QCPAxis::getTickLabelFont() const
5949 return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
5952 /*! \internal
5954 Returns the font that is used to draw the axis label. Depending on the selection state, this
5955 is either mSelectedLabelFont or mLabelFont.
5957 QFont QCPAxis::getLabelFont() const
5959 return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
5962 /*! \internal
5964 Returns the color that is used to draw the tick labels. Depending on the selection state, this
5965 is either mSelectedTickLabelColor or mTickLabelColor.
5967 QColor QCPAxis::getTickLabelColor() const
5969 return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
5972 /*! \internal
5974 Returns the color that is used to draw the axis label. Depending on the selection state, this
5975 is either mSelectedLabelColor or mLabelColor.
5977 QColor QCPAxis::getLabelColor() const
5979 return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
5982 /*! \internal
5984 Returns the appropriate outward margin for this axis. It is needed if \ref
5985 QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref
5986 atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom
5987 margin and so forth. For the calculation, this function goes through similar steps as \ref draw,
5988 so changing one function likely requires the modification of the other one as well.
5990 The margin consists of the outward tick length, tick label padding, tick label size, label
5991 padding, label size, and padding.
5993 The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc.
5994 unchanged are very fast.
5996 int QCPAxis::calculateMargin()
5998 if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
5999 return 0;
6001 if (mCachedMarginValid)
6002 return mCachedMargin;
6004 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6005 int margin = 0;
6007 int lowTick, highTick;
6008 visibleTickBounds(lowTick, highTick);
6009 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6010 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6011 tickPositions.reserve(highTick-lowTick+1);
6012 tickLabels.reserve(highTick-lowTick+1);
6013 if (mTicks)
6015 for (int i=lowTick; i<=highTick; ++i)
6017 tickPositions.append(coordToPixel(mTickVector.at(i)));
6018 if (mTickLabels)
6019 tickLabels.append(mTickVectorLabels.at(i));
6022 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6023 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6024 mAxisPainter->type = mAxisType;
6025 mAxisPainter->labelFont = getLabelFont();
6026 mAxisPainter->label = mLabel;
6027 mAxisPainter->tickLabelFont = mTickLabelFont;
6028 mAxisPainter->axisRect = mAxisRect->rect();
6029 mAxisPainter->viewportRect = mParentPlot->viewport();
6030 mAxisPainter->tickPositions = tickPositions;
6031 mAxisPainter->tickLabels = tickLabels;
6032 margin += mAxisPainter->size();
6033 margin += mPadding;
6035 mCachedMargin = margin;
6036 mCachedMarginValid = true;
6037 return margin;
6040 /* inherits documentation from base class */
6041 QCP::Interaction QCPAxis::selectionCategory() const
6043 return QCP::iSelectAxes;
6047 ////////////////////////////////////////////////////////////////////////////////////////////////////
6048 //////////////////// QCPAxisPainterPrivate
6049 ////////////////////////////////////////////////////////////////////////////////////////////////////
6051 /*! \class QCPAxisPainterPrivate
6053 \internal
6054 \brief (Private)
6056 This is a private class and not part of the public QCustomPlot interface.
6058 It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
6059 axis label. It also buffers the labels to reduce replot times. The parameters are configured by
6060 directly accessing the public member variables.
6064 Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
6065 redraw, to utilize the caching mechanisms.
6067 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
6068 type(QCPAxis::atLeft),
6069 basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6070 lowerEnding(QCPLineEnding::esNone),
6071 upperEnding(QCPLineEnding::esNone),
6072 labelPadding(0),
6073 tickLabelPadding(0),
6074 tickLabelRotation(0),
6075 tickLabelSide(QCPAxis::lsOutside),
6076 substituteExponent(true),
6077 numberMultiplyCross(false),
6078 tickLengthIn(5),
6079 tickLengthOut(0),
6080 subTickLengthIn(2),
6081 subTickLengthOut(0),
6082 tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6083 subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6084 offset(0),
6085 abbreviateDecimalPowers(false),
6086 reversedEndings(false),
6087 mParentPlot(parentPlot),
6088 mLabelCache(16) // cache at most 16 (tick) labels
6092 QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
6096 /*! \internal
6098 Draws the axis with the specified \a painter.
6100 The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
6101 here, too.
6103 void QCPAxisPainterPrivate::draw(QCPPainter *painter)
6105 QByteArray newHash = generateLabelParameterHash();
6106 if (newHash != mLabelParameterHash)
6108 mLabelCache.clear();
6109 mLabelParameterHash = newHash;
6112 QPoint origin;
6113 switch (type)
6115 case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6116 case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6117 case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
6118 case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6121 double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6122 switch (type)
6124 case QCPAxis::atTop: yCor = -1; break;
6125 case QCPAxis::atRight: xCor = 1; break;
6126 default: break;
6128 int margin = 0;
6129 // draw baseline:
6130 QLineF baseLine;
6131 painter->setPen(basePen);
6132 if (QCPAxis::orientation(type) == Qt::Horizontal)
6133 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6134 else
6135 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6136 if (reversedEndings)
6137 baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6138 painter->drawLine(baseLine);
6140 // draw ticks:
6141 if (!tickPositions.isEmpty())
6143 painter->setPen(tickPen);
6144 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6145 if (QCPAxis::orientation(type) == Qt::Horizontal)
6147 for (int i=0; i<tickPositions.size(); ++i)
6148 painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6149 } else
6151 for (int i=0; i<tickPositions.size(); ++i)
6152 painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6156 // draw subticks:
6157 if (!subTickPositions.isEmpty())
6159 painter->setPen(subTickPen);
6160 // direction of ticks ("inward" is right for left axis and left for right axis)
6161 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6162 if (QCPAxis::orientation(type) == Qt::Horizontal)
6164 for (int i=0; i<subTickPositions.size(); ++i)
6165 painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6166 } else
6168 for (int i=0; i<subTickPositions.size(); ++i)
6169 painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6172 margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6174 // draw axis base endings:
6175 bool antialiasingBackup = painter->antialiasing();
6176 painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6177 painter->setBrush(QBrush(basePen.color()));
6178 QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6179 if (lowerEnding.style() != QCPLineEnding::esNone)
6180 lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6181 if (upperEnding.style() != QCPLineEnding::esNone)
6182 upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6183 painter->setAntialiasing(antialiasingBackup);
6185 // tick labels:
6186 QRect oldClipRect;
6187 if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6189 oldClipRect = painter->clipRegion().boundingRect();
6190 painter->setClipRect(axisRect);
6192 QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6193 if (!tickLabels.isEmpty())
6195 if (tickLabelSide == QCPAxis::lsOutside)
6196 margin += tickLabelPadding;
6197 painter->setFont(tickLabelFont);
6198 painter->setPen(QPen(tickLabelColor));
6199 const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6200 int distanceToAxis = margin;
6201 if (tickLabelSide == QCPAxis::lsInside)
6202 distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6203 for (int i=0; i<maxLabelIndex; ++i)
6204 placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6205 if (tickLabelSide == QCPAxis::lsOutside)
6206 margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6208 if (tickLabelSide == QCPAxis::lsInside)
6209 painter->setClipRect(oldClipRect);
6211 // axis label:
6212 QRect labelBounds;
6213 if (!label.isEmpty())
6215 margin += labelPadding;
6216 painter->setFont(labelFont);
6217 painter->setPen(QPen(labelColor));
6218 labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6219 if (type == QCPAxis::atLeft)
6221 QTransform oldTransform = painter->transform();
6222 painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6223 painter->rotate(-90);
6224 painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6225 painter->setTransform(oldTransform);
6227 else if (type == QCPAxis::atRight)
6229 QTransform oldTransform = painter->transform();
6230 painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6231 painter->rotate(90);
6232 painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6233 painter->setTransform(oldTransform);
6235 else if (type == QCPAxis::atTop)
6236 painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6237 else if (type == QCPAxis::atBottom)
6238 painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6241 // set selection boxes:
6242 int selectionTolerance = 0;
6243 if (mParentPlot)
6244 selectionTolerance = mParentPlot->selectionTolerance();
6245 else
6246 qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6247 int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6248 int selAxisInSize = selectionTolerance;
6249 int selTickLabelSize;
6250 int selTickLabelOffset;
6251 if (tickLabelSide == QCPAxis::lsOutside)
6253 selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6254 selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6255 } else
6257 selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6258 selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6260 int selLabelSize = labelBounds.height();
6261 int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6262 if (type == QCPAxis::atLeft)
6264 mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, 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::atRight)
6269 mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6270 mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6271 mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6272 } else if (type == QCPAxis::atTop)
6274 mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
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);
6277 } else if (type == QCPAxis::atBottom)
6279 mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6280 mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6281 mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6283 mAxisSelectionBox = mAxisSelectionBox.normalized();
6284 mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
6285 mLabelSelectionBox = mLabelSelectionBox.normalized();
6286 // draw hitboxes for debug purposes:
6287 //painter->setBrush(Qt::NoBrush);
6288 //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6291 /*! \internal
6293 Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
6294 direction) needed to fit the axis.
6296 int QCPAxisPainterPrivate::size() const
6298 int result = 0;
6300 // get length of tick marks pointing outwards:
6301 if (!tickPositions.isEmpty())
6302 result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6304 // calculate size of tick labels:
6305 if (tickLabelSide == QCPAxis::lsOutside)
6307 QSize tickLabelsSize(0, 0);
6308 if (!tickLabels.isEmpty())
6310 for (int i=0; i<tickLabels.size(); ++i)
6311 getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6312 result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6313 result += tickLabelPadding;
6317 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6318 if (!label.isEmpty())
6320 QFontMetrics fontMetrics(labelFont);
6321 QRect bounds;
6322 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6323 result += bounds.height() + labelPadding;
6326 return result;
6329 /*! \internal
6331 Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
6332 method is called automatically in \ref draw, if any parameters have changed that invalidate the
6333 cached labels, such as font, color, etc.
6335 void QCPAxisPainterPrivate::clearCache()
6337 mLabelCache.clear();
6340 /*! \internal
6342 Returns a hash that allows uniquely identifying whether the label parameters have changed such
6343 that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
6344 return value of this method hasn't changed since the last redraw, the respective label parameters
6345 haven't changed and cached labels may be used.
6347 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
6349 QByteArray result;
6350 result.append(QByteArray::number(tickLabelRotation));
6351 result.append(QByteArray::number((int)tickLabelSide));
6352 result.append(QByteArray::number((int)substituteExponent));
6353 result.append(QByteArray::number((int)numberMultiplyCross));
6354 result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6355 result.append(tickLabelFont.toString().toLatin1());
6356 return result;
6359 /*! \internal
6361 Draws a single tick label with the provided \a painter, utilizing the internal label cache to
6362 significantly speed up drawing of labels that were drawn in previous calls. The tick label is
6363 always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
6364 pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
6365 for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
6366 at which the label should be drawn.
6368 In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
6369 largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
6370 drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
6371 tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
6372 holds.
6374 The label is drawn with the font and pen that are currently set on the \a painter. To draw
6375 superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
6376 getTickLabelData).
6378 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6380 // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6381 if (text.isEmpty()) return;
6382 QSize finalSize;
6383 QPointF labelAnchor;
6384 switch (type)
6386 case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6387 case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6388 case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6389 case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6391 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6393 CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
6394 if (!cachedLabel) // no cached label existed, create it
6396 cachedLabel = new CachedLabel;
6397 TickLabelData labelData = getTickLabelData(painter->font(), text);
6398 cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6399 cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6400 cachedLabel->pixmap.fill(Qt::transparent);
6401 QCPPainter cachePainter(&cachedLabel->pixmap);
6402 cachePainter.setPen(painter->pen());
6403 drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6405 // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6406 bool labelClippedByBorder = false;
6407 if (tickLabelSide == QCPAxis::lsOutside)
6409 if (QCPAxis::orientation(type) == Qt::Horizontal)
6410 labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
6411 else
6412 labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
6414 if (!labelClippedByBorder)
6416 painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6417 finalSize = cachedLabel->pixmap.size();
6419 mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
6420 } else // label caching disabled, draw text directly on surface:
6422 TickLabelData labelData = getTickLabelData(painter->font(), text);
6423 QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6424 // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6425 bool labelClippedByBorder = false;
6426 if (tickLabelSide == QCPAxis::lsOutside)
6428 if (QCPAxis::orientation(type) == Qt::Horizontal)
6429 labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
6430 else
6431 labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
6433 if (!labelClippedByBorder)
6435 drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6436 finalSize = labelData.rotatedTotalBounds.size();
6440 // expand passed tickLabelsSize if current tick label is larger:
6441 if (finalSize.width() > tickLabelsSize->width())
6442 tickLabelsSize->setWidth(finalSize.width());
6443 if (finalSize.height() > tickLabelsSize->height())
6444 tickLabelsSize->setHeight(finalSize.height());
6447 /*! \internal
6449 This is a \ref placeTickLabel helper function.
6451 Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
6452 y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
6453 directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
6454 QCP::phCacheLabels plotting hint is not set.
6456 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6458 // backup painter settings that we're about to change:
6459 QTransform oldTransform = painter->transform();
6460 QFont oldFont = painter->font();
6462 // transform painter to position/rotation:
6463 painter->translate(x, y);
6464 if (!qFuzzyIsNull(tickLabelRotation))
6465 painter->rotate(tickLabelRotation);
6467 // draw text:
6468 if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6470 painter->setFont(labelData.baseFont);
6471 painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6472 painter->setFont(labelData.expFont);
6473 painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6474 } else
6476 painter->setFont(labelData.baseFont);
6477 painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6480 // reset painter settings to what it was before:
6481 painter->setTransform(oldTransform);
6482 painter->setFont(oldFont);
6485 /*! \internal
6487 This is a \ref placeTickLabel helper function.
6489 Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
6490 processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
6491 exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
6493 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
6495 TickLabelData result;
6497 // determine whether beautiful decimal powers should be used
6498 bool useBeautifulPowers = false;
6499 int ePos = -1;
6500 if (substituteExponent)
6502 ePos = text.indexOf(QLatin1Char('e'));
6503 if (ePos > -1)
6504 useBeautifulPowers = true;
6507 // calculate text bounding rects and do string preparation for beautiful decimal powers:
6508 result.baseFont = font;
6509 if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
6510 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
6511 if (useBeautifulPowers)
6513 // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6514 result.basePart = text.left(ePos);
6515 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6516 if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6517 result.basePart = QLatin1String("10");
6518 else
6519 result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6520 result.expPart = text.mid(ePos+1);
6521 // clip "+" and leading zeros off expPart:
6522 while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6523 result.expPart.remove(1, 1);
6524 if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6525 result.expPart.remove(0, 1);
6526 // prepare smaller font for exponent:
6527 result.expFont = font;
6528 if (result.expFont.pointSize() > 0)
6529 result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6530 else
6531 result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
6532 // calculate bounding rects of base part, exponent part and total one:
6533 result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6534 result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6535 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
6536 } else // useBeautifulPowers == false
6538 result.basePart = text;
6539 result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6541 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
6543 // calculate possibly different bounding rect after rotation:
6544 result.rotatedTotalBounds = result.totalBounds;
6545 if (!qFuzzyIsNull(tickLabelRotation))
6547 QTransform transform;
6548 transform.rotate(tickLabelRotation);
6549 result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6552 return result;
6555 /*! \internal
6557 This is a \ref placeTickLabel helper function.
6559 Calculates the offset at which the top left corner of the specified tick label shall be drawn.
6560 The offset is relative to a point right next to the tick the label belongs to.
6562 This function is thus responsible for e.g. centering tick labels under ticks and positioning them
6563 appropriately when they are rotated.
6565 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
6568 calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6569 explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6570 horizontally under the corresponding tick is always on the label side that is closer to the
6571 axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6572 is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6573 will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6574 time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6575 labels.
6577 bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6578 bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6579 double radians = tickLabelRotation/180.0*M_PI;
6580 int x=0, y=0;
6581 if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6583 if (doRotation)
6585 if (tickLabelRotation > 0)
6587 x = -qCos(radians)*labelData.totalBounds.width();
6588 y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6589 } else
6591 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6592 y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6594 } else
6596 x = -labelData.totalBounds.width();
6597 y = -labelData.totalBounds.height()/2.0;
6599 } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6601 if (doRotation)
6603 if (tickLabelRotation > 0)
6605 x = +qSin(radians)*labelData.totalBounds.height();
6606 y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6607 } else
6609 x = 0;
6610 y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6612 } else
6614 x = 0;
6615 y = -labelData.totalBounds.height()/2.0;
6617 } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6619 if (doRotation)
6621 if (tickLabelRotation > 0)
6623 x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6624 y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6625 } else
6627 x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6628 y = -qCos(-radians)*labelData.totalBounds.height();
6630 } else
6632 x = -labelData.totalBounds.width()/2.0;
6633 y = -labelData.totalBounds.height();
6635 } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6637 if (doRotation)
6639 if (tickLabelRotation > 0)
6641 x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6642 y = 0;
6643 } else
6645 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6646 y = +qSin(-radians)*labelData.totalBounds.width();
6648 } else
6650 x = -labelData.totalBounds.width()/2.0;
6651 y = 0;
6655 return QPointF(x, y);
6658 /*! \internal
6660 Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
6661 to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
6662 margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
6663 smaller width/height.
6665 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6667 // note: this function must return the same tick label sizes as the placeTickLabel function.
6668 QSize finalSize;
6669 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6671 const CachedLabel *cachedLabel = mLabelCache.object(text);
6672 finalSize = cachedLabel->pixmap.size();
6673 } else // label caching disabled or no label with this text cached:
6675 TickLabelData labelData = getTickLabelData(font, text);
6676 finalSize = labelData.rotatedTotalBounds.size();
6679 // expand passed tickLabelsSize if current tick label is larger:
6680 if (finalSize.width() > tickLabelsSize->width())
6681 tickLabelsSize->setWidth(finalSize.width());
6682 if (finalSize.height() > tickLabelsSize->height())
6683 tickLabelsSize->setHeight(finalSize.height());
6687 ////////////////////////////////////////////////////////////////////////////////////////////////////
6688 //////////////////// QCPAbstractPlottable
6689 ////////////////////////////////////////////////////////////////////////////////////////////////////
6691 /*! \class QCPAbstractPlottable
6692 \brief The abstract base class for all data representing objects in a plot.
6694 It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
6695 abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to
6696 create new ways of displaying data (see "Creating own plottables" below).
6698 All further specifics are in the subclasses, for example:
6699 \li A normal graph with possibly a line, scatter points and error bars: \ref QCPGraph
6700 (typically created with \ref QCustomPlot::addGraph)
6701 \li A parametric curve: \ref QCPCurve
6702 \li A bar chart: \ref QCPBars
6703 \li A statistical box plot: \ref QCPStatisticalBox
6704 \li A color encoded two-dimensional map: \ref QCPColorMap
6705 \li An OHLC/Candlestick chart: \ref QCPFinancial
6707 \section plottables-subclassing Creating own plottables
6709 To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
6710 virtual functions, you must implement:
6711 \li \ref clearData
6712 \li \ref selectTest
6713 \li \ref draw
6714 \li \ref drawLegendIcon
6715 \li \ref getKeyRange
6716 \li \ref getValueRange
6718 See the documentation of those functions for what they need to do.
6720 For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
6721 coordinates to pixel coordinates. This function is quite convenient, because it takes the
6722 orientation of the key and value axes into account for you (x and y are swapped when the key axis
6723 is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
6724 to translate many points in a loop like QCPGraph), you can directly use \ref
6725 QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
6726 yourself.
6728 Here are some important members you inherit from QCPAbstractPlottable:
6729 <table>
6730 <tr>
6731 <td>QCustomPlot *\b mParentPlot</td>
6732 <td>A pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.</td>
6733 </tr><tr>
6734 <td>QString \b mName</td>
6735 <td>The name of the plottable.</td>
6736 </tr><tr>
6737 <td>QPen \b mPen</td>
6738 <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>
6739 </tr><tr>
6740 <td>QPen \b mSelectedPen</td>
6741 <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>
6742 </tr><tr>
6743 <td>QBrush \b mBrush</td>
6744 <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>
6745 </tr><tr>
6746 <td>QBrush \b mSelectedBrush</td>
6747 <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>
6748 </tr><tr>
6749 <td>QPointer<QCPAxis>\b mKeyAxis, \b mValueAxis</td>
6750 <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.
6751 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>
6752 </tr><tr>
6753 <td>bool \b mSelected</td>
6754 <td>indicates whether the plottable is selected or not.</td>
6755 </tr>
6756 </table>
6759 /* start of documentation of pure virtual functions */
6761 /*! \fn void QCPAbstractPlottable::clearData() = 0
6762 Clears all data in the plottable.
6765 /*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
6766 \internal
6768 called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
6769 of this plottable inside \a rect, next to the plottable name.
6771 The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't
6772 appear outside the legend icon border.
6775 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
6776 \internal
6778 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6779 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6780 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6781 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6782 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6783 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6784 use the returned range (e.g. no points in data).
6786 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6787 this function may have size zero, which wouldn't count as a valid range.
6789 \see rescaleAxes, getValueRange
6792 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
6793 \internal
6795 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6796 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6797 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6798 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6799 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6800 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6801 use the returned range (e.g. no points in data).
6803 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6804 this function may have size zero, which wouldn't count as a valid range.
6806 \see rescaleAxes, getKeyRange
6809 /* end of documentation of pure virtual functions */
6810 /* start of documentation of signals */
6812 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
6814 This signal is emitted when the selection state of this plottable has changed, either by user
6815 interaction or by a direct call to \ref setSelected.
6818 /*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
6820 This signal is emitted when the selectability of this plottable has changed.
6822 \see setSelectable
6825 /* end of documentation of signals */
6828 Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
6829 its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
6830 and have perpendicular orientations. If either of these restrictions is violated, a corresponding
6831 message is printed to the debug output (qDebug), the construction is not aborted, though.
6833 Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables,
6834 it can't be directly instantiated.
6836 You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead.
6838 QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
6839 QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6840 mName(),
6841 mAntialiasedFill(true),
6842 mAntialiasedScatters(true),
6843 mAntialiasedErrorBars(false),
6844 mPen(Qt::black),
6845 mSelectedPen(Qt::black),
6846 mBrush(Qt::NoBrush),
6847 mSelectedBrush(Qt::NoBrush),
6848 mKeyAxis(keyAxis),
6849 mValueAxis(valueAxis),
6850 mSelectable(true),
6851 mSelected(false)
6853 if (keyAxis->parentPlot() != valueAxis->parentPlot())
6854 qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6855 if (keyAxis->orientation() == valueAxis->orientation())
6856 qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6860 The name is the textual representation of this plottable as it is displayed in the legend
6861 (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
6863 void QCPAbstractPlottable::setName(const QString &name)
6865 mName = name;
6869 Sets whether fills of this plottable are drawn antialiased or not.
6871 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6872 QCustomPlot::setNotAntialiasedElements.
6874 void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
6876 mAntialiasedFill = enabled;
6880 Sets whether the scatter symbols of this plottable are drawn antialiased or not.
6882 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6883 QCustomPlot::setNotAntialiasedElements.
6885 void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
6887 mAntialiasedScatters = enabled;
6891 Sets whether the error bars of this plottable are drawn antialiased or not.
6893 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6894 QCustomPlot::setNotAntialiasedElements.
6896 void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
6898 mAntialiasedErrorBars = enabled;
6903 The pen is used to draw basic lines that make up the plottable representation in the
6904 plot.
6906 For example, the \ref QCPGraph subclass draws its graph lines with this pen.
6908 \see setBrush
6910 void QCPAbstractPlottable::setPen(const QPen &pen)
6912 mPen = pen;
6916 When the plottable is selected, this pen is used to draw basic lines instead of the normal
6917 pen set via \ref setPen.
6919 \see setSelected, setSelectable, setSelectedBrush, selectTest
6921 void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
6923 mSelectedPen = pen;
6927 The brush is used to draw basic fills of the plottable representation in the
6928 plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
6930 For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
6931 it's not set to Qt::NoBrush.
6933 \see setPen
6935 void QCPAbstractPlottable::setBrush(const QBrush &brush)
6937 mBrush = brush;
6941 When the plottable is selected, this brush is used to draw fills instead of the normal
6942 brush set via \ref setBrush.
6944 \see setSelected, setSelectable, setSelectedPen, selectTest
6946 void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
6948 mSelectedBrush = brush;
6952 The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
6953 to the plottable's value axis. This function performs no checks to make sure this is the case.
6954 The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
6955 y-axis (QCustomPlot::yAxis) as value axis.
6957 Normally, the key and value axes are set in the constructor of the plottable (or \ref
6958 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6960 \see setValueAxis
6962 void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
6964 mKeyAxis = axis;
6968 The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
6969 orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
6970 case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
6971 the y-axis (QCustomPlot::yAxis) as value axis.
6973 Normally, the key and value axes are set in the constructor of the plottable (or \ref
6974 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6976 \see setKeyAxis
6978 void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
6980 mValueAxis = axis;
6984 Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
6985 (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
6987 However, even when \a selectable was set to false, it is possible to set the selection manually,
6988 by calling \ref setSelected directly.
6990 \see setSelected
6992 void QCPAbstractPlottable::setSelectable(bool selectable)
6994 if (mSelectable != selectable)
6996 mSelectable = selectable;
6997 emit selectableChanged(mSelectable);
7002 Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
7003 to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
7005 The entire selection mechanism for plottables is handled automatically when \ref
7006 QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
7007 you wish to change the selection state manually.
7009 This function can change the selection state even when \ref setSelectable was set to false.
7011 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
7013 \see setSelectable, selectTest
7015 void QCPAbstractPlottable::setSelected(bool selected)
7017 if (mSelected != selected)
7019 mSelected = selected;
7020 emit selectionChanged(mSelected);
7025 Rescales the key and value axes associated with this plottable to contain all displayed data, so
7026 the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
7027 sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
7028 Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
7029 outside of that domain.
7031 \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
7032 multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
7033 \a onlyEnlarge set to false (the default), and all subsequent set to true.
7035 \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale
7037 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7039 rescaleKeyAxis(onlyEnlarge);
7040 rescaleValueAxis(onlyEnlarge);
7044 Rescales the key axis of the plottable so the whole plottable is visible.
7046 See \ref rescaleAxes for detailed behaviour.
7048 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7050 QCPAxis *keyAxis = mKeyAxis.data();
7051 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7053 SignDomain signDomain = sdBoth;
7054 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7055 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7057 bool foundRange;
7058 QCPRange newRange = getKeyRange(foundRange, signDomain);
7059 if (foundRange)
7061 if (onlyEnlarge)
7062 newRange.expand(keyAxis->range());
7063 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
7065 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
7066 if (keyAxis->scaleType() == QCPAxis::stLinear)
7068 newRange.lower = center-keyAxis->range().size()/2.0;
7069 newRange.upper = center+keyAxis->range().size()/2.0;
7070 } else // scaleType() == stLogarithmic
7072 newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7073 newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7076 keyAxis->setRange(newRange);
7081 Rescales the value axis of the plottable so the whole plottable is visible.
7083 Returns true if the axis was actually scaled. This might not be the case if this plottable has an
7084 invalid range, e.g. because it has no data points.
7086 See \ref rescaleAxes for detailed behaviour.
7088 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7090 QCPAxis *valueAxis = mValueAxis.data();
7091 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7093 SignDomain signDomain = sdBoth;
7094 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7095 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7097 bool foundRange;
7098 QCPRange newRange = getValueRange(foundRange, signDomain);
7099 if (foundRange)
7101 if (onlyEnlarge)
7102 newRange.expand(valueAxis->range());
7103 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
7105 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
7106 if (valueAxis->scaleType() == QCPAxis::stLinear)
7108 newRange.lower = center-valueAxis->range().size()/2.0;
7109 newRange.upper = center+valueAxis->range().size()/2.0;
7110 } else // scaleType() == stLogarithmic
7112 newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7113 newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7116 valueAxis->setRange(newRange);
7121 Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
7123 Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
7124 needs a more specialized representation in the legend, this function will take this into account
7125 and instead create the specialized subclass of QCPAbstractLegendItem.
7127 Returns true on success, i.e. when the legend exists and a legend item associated with this plottable isn't already in
7128 the legend.
7130 \see removeFromLegend, QCPLegend::addItem
7132 bool QCPAbstractPlottable::addToLegend()
7134 if (!mParentPlot || !mParentPlot->legend)
7135 return false;
7137 if (!mParentPlot->legend->hasItemWithPlottable(this))
7139 mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
7140 return true;
7141 } else
7142 return false;
7146 Removes the plottable from the legend of the parent QCustomPlot. This means the
7147 QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
7148 is removed.
7150 Returns true on success, i.e. if the legend exists and a legend item associated with this
7151 plottable was found and removed.
7153 \see addToLegend, QCPLegend::removeItem
7155 bool QCPAbstractPlottable::removeFromLegend() const
7157 if (!mParentPlot->legend)
7158 return false;
7160 if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
7161 return mParentPlot->legend->removeItem(lip);
7162 else
7163 return false;
7166 /* inherits documentation from base class */
7167 QRect QCPAbstractPlottable::clipRect() const
7169 if (mKeyAxis && mValueAxis)
7170 return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7171 else
7172 return QRect();
7175 /* inherits documentation from base class */
7176 QCP::Interaction QCPAbstractPlottable::selectionCategory() const
7178 return QCP::iSelectPlottables;
7181 /*! \internal
7183 Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
7184 taking the orientations of the axes associated with this plottable into account (e.g. whether key
7185 represents x or y).
7187 \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
7189 \see pixelsToCoords, QCPAxis::coordToPixel
7191 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7193 QCPAxis *keyAxis = mKeyAxis.data();
7194 QCPAxis *valueAxis = mValueAxis.data();
7195 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7197 if (keyAxis->orientation() == Qt::Horizontal)
7199 x = keyAxis->coordToPixel(key);
7200 y = valueAxis->coordToPixel(value);
7201 } else
7203 y = keyAxis->coordToPixel(key);
7204 x = valueAxis->coordToPixel(value);
7208 /*! \internal
7209 \overload
7211 Returns the input as pixel coordinates in a QPointF.
7213 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7215 QCPAxis *keyAxis = mKeyAxis.data();
7216 QCPAxis *valueAxis = mValueAxis.data();
7217 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7219 if (keyAxis->orientation() == Qt::Horizontal)
7220 return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7221 else
7222 return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7225 /*! \internal
7227 Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
7228 taking the orientations of the axes associated with this plottable into account (e.g. whether key
7229 represents x or y).
7231 \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
7233 \see coordsToPixels, QCPAxis::coordToPixel
7235 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7237 QCPAxis *keyAxis = mKeyAxis.data();
7238 QCPAxis *valueAxis = mValueAxis.data();
7239 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7241 if (keyAxis->orientation() == Qt::Horizontal)
7243 key = keyAxis->pixelToCoord(x);
7244 value = valueAxis->pixelToCoord(y);
7245 } else
7247 key = keyAxis->pixelToCoord(y);
7248 value = valueAxis->pixelToCoord(x);
7252 /*! \internal
7253 \overload
7255 Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
7257 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7259 pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7262 /*! \internal
7264 Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
7265 graph is not selected and mSelectedPen when it is.
7267 QPen QCPAbstractPlottable::mainPen() const
7269 return mSelected ? mSelectedPen : mPen;
7272 /*! \internal
7274 Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
7275 graph is not selected and mSelectedBrush when it is.
7277 QBrush QCPAbstractPlottable::mainBrush() const
7279 return mSelected ? mSelectedBrush : mBrush;
7282 /*! \internal
7284 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7285 before drawing plottable lines.
7287 This is the antialiasing state the painter passed to the \ref draw method is in by default.
7289 This function takes into account the local setting of the antialiasing flag as well as the
7290 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7291 QCustomPlot::setNotAntialiasedElements.
7293 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7295 void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
7297 applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
7300 /*! \internal
7302 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7303 before drawing plottable fills.
7305 This function takes into account the local setting of the antialiasing flag as well as the
7306 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7307 QCustomPlot::setNotAntialiasedElements.
7309 \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7311 void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
7313 applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
7316 /*! \internal
7318 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7319 before drawing plottable scatter points.
7321 This function takes into account the local setting of the antialiasing flag as well as the
7322 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7323 QCustomPlot::setNotAntialiasedElements.
7325 \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
7327 void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
7329 applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
7332 /*! \internal
7334 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7335 before drawing plottable error bars.
7337 This function takes into account the local setting of the antialiasing flag as well as the
7338 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7339 QCustomPlot::setNotAntialiasedElements.
7341 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
7343 void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
7345 applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
7348 /*! \internal
7350 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
7351 end.
7353 This function may be used to help with the implementation of the \ref selectTest function for
7354 specific plottables.
7356 \note This function is identical to QCPAbstractItem::distSqrToLine
7358 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7360 QVector2D a(start);
7361 QVector2D b(end);
7362 QVector2D p(point);
7363 QVector2D v(b-a);
7365 double vLengthSqr = v.lengthSquared();
7366 if (!qFuzzyIsNull(vLengthSqr))
7368 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7369 if (mu < 0)
7370 return (a-p).lengthSquared();
7371 else if (mu > 1)
7372 return (b-p).lengthSquared();
7373 else
7374 return ((a + mu*v)-p).lengthSquared();
7375 } else
7376 return (a-p).lengthSquared();
7379 /* inherits documentation from base class */
7380 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7382 Q_UNUSED(event)
7383 Q_UNUSED(details)
7384 if (mSelectable)
7386 bool selBefore = mSelected;
7387 setSelected(additive ? !mSelected : true);
7388 if (selectionStateChanged)
7389 *selectionStateChanged = mSelected != selBefore;
7393 /* inherits documentation from base class */
7394 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7396 if (mSelectable)
7398 bool selBefore = mSelected;
7399 setSelected(false);
7400 if (selectionStateChanged)
7401 *selectionStateChanged = mSelected != selBefore;
7406 ////////////////////////////////////////////////////////////////////////////////////////////////////
7407 //////////////////// QCPItemAnchor
7408 ////////////////////////////////////////////////////////////////////////////////////////////////////
7410 /*! \class QCPItemAnchor
7411 \brief An anchor of an item to which positions can be attached to.
7413 An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
7414 control anything on its item, but provides a way to tie other items via their positions to the
7415 anchor.
7417 For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
7418 Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
7419 attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
7420 calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
7421 QCPItemRect. This way the start of the line will now always follow the respective anchor location
7422 on the rect item.
7424 Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
7425 anchor to other positions.
7427 To learn how to provide anchors in your own item subclasses, see the subclassing section of the
7428 QCPAbstractItem documentation.
7431 /* start documentation of inline functions */
7433 /*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
7435 Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if
7436 it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
7438 This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids
7439 dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with
7440 gcc compiler).
7443 /* end documentation of inline functions */
7446 Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
7447 you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
7448 explained in the subclassing section of the QCPAbstractItem documentation.
7450 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7451 mName(name),
7452 mParentPlot(parentPlot),
7453 mParentItem(parentItem),
7454 mAnchorId(anchorId)
7458 QCPItemAnchor::~QCPItemAnchor()
7460 // unregister as parent at children:
7461 foreach (QCPItemPosition *child, mChildrenX.toList())
7463 if (child->parentAnchorX() == this)
7464 child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7466 foreach (QCPItemPosition *child, mChildrenY.toList())
7468 if (child->parentAnchorY() == this)
7469 child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7474 Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
7476 The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
7477 parent item, QCPItemAnchor is just an intermediary.
7479 QPointF QCPItemAnchor::pixelPoint() const
7481 if (mParentItem)
7483 if (mAnchorId > -1)
7485 return mParentItem->anchorPixelPoint(mAnchorId);
7486 } else
7488 qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7489 return QPointF();
7491 } else
7493 qDebug() << Q_FUNC_INFO << "no parent item set";
7494 return QPointF();
7498 /*! \internal
7500 Adds \a pos to the childX list of this anchor, which keeps track of which children use this
7501 anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7502 prior to destruction of the anchor.
7504 Note that this function does not change the parent setting in \a pos.
7506 void QCPItemAnchor::addChildX(QCPItemPosition *pos)
7508 if (!mChildrenX.contains(pos))
7509 mChildrenX.insert(pos);
7510 else
7511 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7514 /*! \internal
7516 Removes \a pos from the childX list of this anchor.
7518 Note that this function does not change the parent setting in \a pos.
7520 void QCPItemAnchor::removeChildX(QCPItemPosition *pos)
7522 if (!mChildrenX.remove(pos))
7523 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7526 /*! \internal
7528 Adds \a pos to the childY list of this anchor, which keeps track of which children use this
7529 anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7530 prior to destruction of the anchor.
7532 Note that this function does not change the parent setting in \a pos.
7534 void QCPItemAnchor::addChildY(QCPItemPosition *pos)
7536 if (!mChildrenY.contains(pos))
7537 mChildrenY.insert(pos);
7538 else
7539 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7542 /*! \internal
7544 Removes \a pos from the childY list of this anchor.
7546 Note that this function does not change the parent setting in \a pos.
7548 void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
7550 if (!mChildrenY.remove(pos))
7551 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7555 ////////////////////////////////////////////////////////////////////////////////////////////////////
7556 //////////////////// QCPItemPosition
7557 ////////////////////////////////////////////////////////////////////////////////////////////////////
7559 /*! \class QCPItemPosition
7560 \brief Manages the position of an item.
7562 Every item has at least one public QCPItemPosition member pointer which provides ways to position the
7563 item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
7564 \a topLeft and \a bottomRight.
7566 QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type
7567 defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel
7568 coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also
7569 possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref
7570 setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y
7571 direction, while following a plot coordinate in the X direction.
7573 A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie
7574 multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords)
7575 are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0)
7576 means directly ontop of the parent anchor. For example, You could attach the \a start position of
7577 a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line
7578 always be centered under the text label, no matter where the text is moved to. For more advanced
7579 plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see
7580 \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X
7581 direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B
7582 in Y.
7584 Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent
7585 anchor for other positions.
7587 To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
7588 works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
7589 setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
7590 pixel values.
7593 /* start documentation of inline functions */
7595 /*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const
7597 Returns the current position type.
7599 If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the
7600 type of the X coordinate. In that case rather use \a typeX() and \a typeY().
7602 \see setType
7605 /*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const
7607 Returns the current parent anchor.
7609 If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY),
7610 this method returns the parent anchor of the Y coordinate. In that case rather use \a
7611 parentAnchorX() and \a parentAnchorY().
7613 \see setParentAnchor
7616 /* end documentation of inline functions */
7619 Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
7620 you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
7621 explained in the subclassing section of the QCPAbstractItem documentation.
7623 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7624 QCPItemAnchor(parentPlot, parentItem, name),
7625 mPositionTypeX(ptAbsolute),
7626 mPositionTypeY(ptAbsolute),
7627 mKey(0),
7628 mValue(0),
7629 mParentAnchorX(0),
7630 mParentAnchorY(0)
7634 QCPItemPosition::~QCPItemPosition()
7636 // unregister as parent at children:
7637 // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7638 // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7639 foreach (QCPItemPosition *child, mChildrenX.toList())
7641 if (child->parentAnchorX() == this)
7642 child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7644 foreach (QCPItemPosition *child, mChildrenY.toList())
7646 if (child->parentAnchorY() == this)
7647 child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7649 // unregister as child in parent:
7650 if (mParentAnchorX)
7651 mParentAnchorX->removeChildX(this);
7652 if (mParentAnchorY)
7653 mParentAnchorY->removeChildY(this);
7656 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7657 QCPAxisRect *QCPItemPosition::axisRect() const
7659 return mAxisRect.data();
7663 Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
7664 should be handled and how the QCPItemPosition should behave in the plot.
7666 The possible values for \a type can be separated in two main categories:
7668 \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
7669 and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
7670 By default, the QCustomPlot's x- and yAxis are used.
7672 \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This
7673 corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref
7674 ptAxisRectRatio. They differ only in the way the absolute position is described, see the
7675 documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify
7676 the axis rect with \ref setAxisRect. By default this is set to the main axis rect.
7678 Note that the position type \ref ptPlotCoords is only available (and sensible) when the position
7679 has no parent anchor (\ref setParentAnchor).
7681 If the type is changed, the apparent pixel position on the plot is preserved. This means
7682 the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
7684 This method sets the type for both X and Y directions. It is also possible to set different types
7685 for X and Y, see \ref setTypeX, \ref setTypeY.
7687 void QCPItemPosition::setType(QCPItemPosition::PositionType type)
7689 setTypeX(type);
7690 setTypeY(type);
7694 This method sets the position type of the X coordinate to \a type.
7696 For a detailed description of what a position type is, see the documentation of \ref setType.
7698 \see setType, setTypeY
7700 void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)
7702 if (mPositionTypeX != type)
7704 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7705 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7706 bool retainPixelPosition = true;
7707 if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7708 retainPixelPosition = false;
7709 if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7710 retainPixelPosition = false;
7712 QPointF pixel;
7713 if (retainPixelPosition)
7714 pixel = pixelPoint();
7716 mPositionTypeX = type;
7718 if (retainPixelPosition)
7719 setPixelPoint(pixel);
7724 This method sets the position type of the Y coordinate to \a type.
7726 For a detailed description of what a position type is, see the documentation of \ref setType.
7728 \see setType, setTypeX
7730 void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
7732 if (mPositionTypeY != type)
7734 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7735 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7736 bool retainPixelPosition = true;
7737 if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7738 retainPixelPosition = false;
7739 if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7740 retainPixelPosition = false;
7742 QPointF pixel;
7743 if (retainPixelPosition)
7744 pixel = pixelPoint();
7746 mPositionTypeY = type;
7748 if (retainPixelPosition)
7749 setPixelPoint(pixel);
7754 Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
7755 follow any position changes of the anchor. The local coordinate system of positions with a parent
7756 anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence
7757 the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.)
7759 if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
7760 during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
7761 will be exactly on top of the parent anchor.
7763 To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
7765 If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is
7766 set to \ref ptAbsolute, to keep the position in a valid state.
7768 This method sets the parent anchor for both X and Y directions. It is also possible to set
7769 different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY.
7771 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7773 bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7774 bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7775 return successX && successY;
7779 This method sets the parent anchor of the X coordinate to \a parentAnchor.
7781 For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7783 \see setParentAnchor, setParentAnchorY
7785 bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7787 // make sure self is not assigned as parent:
7788 if (parentAnchor == this)
7790 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7791 return false;
7793 // make sure no recursive parent-child-relationships are created:
7794 QCPItemAnchor *currentParent = parentAnchor;
7795 while (currentParent)
7797 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7799 // is a QCPItemPosition, might have further parent, so keep iterating
7800 if (currentParentPos == this)
7802 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7803 return false;
7805 currentParent = currentParentPos->parentAnchorX();
7806 } else
7808 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7809 // same, to prevent a position being child of an anchor which itself depends on the position,
7810 // because they're both on the same item:
7811 if (currentParent->mParentItem == mParentItem)
7813 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7814 return false;
7816 break;
7820 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7821 if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)
7822 setTypeX(ptAbsolute);
7824 // save pixel position:
7825 QPointF pixelP;
7826 if (keepPixelPosition)
7827 pixelP = pixelPoint();
7828 // unregister at current parent anchor:
7829 if (mParentAnchorX)
7830 mParentAnchorX->removeChildX(this);
7831 // register at new parent anchor:
7832 if (parentAnchor)
7833 parentAnchor->addChildX(this);
7834 mParentAnchorX = parentAnchor;
7835 // restore pixel position under new parent:
7836 if (keepPixelPosition)
7837 setPixelPoint(pixelP);
7838 else
7839 setCoords(0, coords().y());
7840 return true;
7844 This method sets the parent anchor of the Y coordinate to \a parentAnchor.
7846 For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7848 \see setParentAnchor, setParentAnchorX
7850 bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7852 // make sure self is not assigned as parent:
7853 if (parentAnchor == this)
7855 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7856 return false;
7858 // make sure no recursive parent-child-relationships are created:
7859 QCPItemAnchor *currentParent = parentAnchor;
7860 while (currentParent)
7862 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7864 // is a QCPItemPosition, might have further parent, so keep iterating
7865 if (currentParentPos == this)
7867 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7868 return false;
7870 currentParent = currentParentPos->parentAnchorY();
7871 } else
7873 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7874 // same, to prevent a position being child of an anchor which itself depends on the position,
7875 // because they're both on the same item:
7876 if (currentParent->mParentItem == mParentItem)
7878 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7879 return false;
7881 break;
7885 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7886 if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)
7887 setTypeY(ptAbsolute);
7889 // save pixel position:
7890 QPointF pixelP;
7891 if (keepPixelPosition)
7892 pixelP = pixelPoint();
7893 // unregister at current parent anchor:
7894 if (mParentAnchorY)
7895 mParentAnchorY->removeChildY(this);
7896 // register at new parent anchor:
7897 if (parentAnchor)
7898 parentAnchor->addChildY(this);
7899 mParentAnchorY = parentAnchor;
7900 // restore pixel position under new parent:
7901 if (keepPixelPosition)
7902 setPixelPoint(pixelP);
7903 else
7904 setCoords(coords().x(), 0);
7905 return true;
7909 Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
7910 (\ref setType, \ref setTypeX, \ref setTypeY).
7912 For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
7913 on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the
7914 QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the
7915 plot coordinate system defined by the axes set by \ref setAxes. By default those are the
7916 QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available
7917 coordinate types and their meaning.
7919 If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a
7920 value must also be provided in the different coordinate systems. Here, the X type refers to \a
7921 key, and the Y type refers to \a value.
7923 \see setPixelPoint
7925 void QCPItemPosition::setCoords(double key, double value)
7927 mKey = key;
7928 mValue = value;
7931 /*! \overload
7933 Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
7934 meaning of \a value of the \ref setCoords(double key, double value) method.
7936 void QCPItemPosition::setCoords(const QPointF &pos)
7938 setCoords(pos.x(), pos.y());
7942 Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
7943 includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
7945 \see setPixelPoint
7947 QPointF QCPItemPosition::pixelPoint() const
7949 QPointF result;
7951 // determine X:
7952 switch (mPositionTypeX)
7954 case ptAbsolute:
7956 result.rx() = mKey;
7957 if (mParentAnchorX)
7958 result.rx() += mParentAnchorX->pixelPoint().x();
7959 break;
7961 case ptViewportRatio:
7963 result.rx() = mKey*mParentPlot->viewport().width();
7964 if (mParentAnchorX)
7965 result.rx() += mParentAnchorX->pixelPoint().x();
7966 else
7967 result.rx() += mParentPlot->viewport().left();
7968 break;
7970 case ptAxisRectRatio:
7972 if (mAxisRect)
7974 result.rx() = mKey*mAxisRect.data()->width();
7975 if (mParentAnchorX)
7976 result.rx() += mParentAnchorX->pixelPoint().x();
7977 else
7978 result.rx() += mAxisRect.data()->left();
7979 } else
7980 qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
7981 break;
7983 case ptPlotCoords:
7985 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
7986 result.rx() = mKeyAxis.data()->coordToPixel(mKey);
7987 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
7988 result.rx() = mValueAxis.data()->coordToPixel(mValue);
7989 else
7990 qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
7991 break;
7995 // determine Y:
7996 switch (mPositionTypeY)
7998 case ptAbsolute:
8000 result.ry() = mValue;
8001 if (mParentAnchorY)
8002 result.ry() += mParentAnchorY->pixelPoint().y();
8003 break;
8005 case ptViewportRatio:
8007 result.ry() = mValue*mParentPlot->viewport().height();
8008 if (mParentAnchorY)
8009 result.ry() += mParentAnchorY->pixelPoint().y();
8010 else
8011 result.ry() += mParentPlot->viewport().top();
8012 break;
8014 case ptAxisRectRatio:
8016 if (mAxisRect)
8018 result.ry() = mValue*mAxisRect.data()->height();
8019 if (mParentAnchorY)
8020 result.ry() += mParentAnchorY->pixelPoint().y();
8021 else
8022 result.ry() += mAxisRect.data()->top();
8023 } else
8024 qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8025 break;
8027 case ptPlotCoords:
8029 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8030 result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8031 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8032 result.ry() = mValueAxis.data()->coordToPixel(mValue);
8033 else
8034 qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8035 break;
8039 return result;
8043 When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the
8044 coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and
8045 yAxis of the QCustomPlot.
8047 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
8049 mKeyAxis = keyAxis;
8050 mValueAxis = valueAxis;
8054 When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the
8055 coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of
8056 the QCustomPlot.
8058 void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)
8060 mAxisRect = axisRect;
8064 Sets the apparent pixel position. This works no matter what type (\ref setType) this
8065 QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed
8066 appropriately, to make the position finally appear at the specified pixel values.
8068 Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is
8069 identical to that of \ref setCoords.
8071 \see pixelPoint, setCoords
8073 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
8075 double x = pixelPoint.x();
8076 double y = pixelPoint.y();
8078 switch (mPositionTypeX)
8080 case ptAbsolute:
8082 if (mParentAnchorX)
8083 x -= mParentAnchorX->pixelPoint().x();
8084 break;
8086 case ptViewportRatio:
8088 if (mParentAnchorX)
8089 x -= mParentAnchorX->pixelPoint().x();
8090 else
8091 x -= mParentPlot->viewport().left();
8092 x /= (double)mParentPlot->viewport().width();
8093 break;
8095 case ptAxisRectRatio:
8097 if (mAxisRect)
8099 if (mParentAnchorX)
8100 x -= mParentAnchorX->pixelPoint().x();
8101 else
8102 x -= mAxisRect.data()->left();
8103 x /= (double)mAxisRect.data()->width();
8104 } else
8105 qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8106 break;
8108 case ptPlotCoords:
8110 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8111 x = mKeyAxis.data()->pixelToCoord(x);
8112 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8113 y = mValueAxis.data()->pixelToCoord(x);
8114 else
8115 qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8116 break;
8120 switch (mPositionTypeY)
8122 case ptAbsolute:
8124 if (mParentAnchorY)
8125 y -= mParentAnchorY->pixelPoint().y();
8126 break;
8128 case ptViewportRatio:
8130 if (mParentAnchorY)
8131 y -= mParentAnchorY->pixelPoint().y();
8132 else
8133 y -= mParentPlot->viewport().top();
8134 y /= (double)mParentPlot->viewport().height();
8135 break;
8137 case ptAxisRectRatio:
8139 if (mAxisRect)
8141 if (mParentAnchorY)
8142 y -= mParentAnchorY->pixelPoint().y();
8143 else
8144 y -= mAxisRect.data()->top();
8145 y /= (double)mAxisRect.data()->height();
8146 } else
8147 qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8148 break;
8150 case ptPlotCoords:
8152 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8153 x = mKeyAxis.data()->pixelToCoord(y);
8154 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8155 y = mValueAxis.data()->pixelToCoord(y);
8156 else
8157 qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8158 break;
8162 setCoords(x, y);
8166 ////////////////////////////////////////////////////////////////////////////////////////////////////
8167 //////////////////// QCPAbstractItem
8168 ////////////////////////////////////////////////////////////////////////////////////////////////////
8170 /*! \class QCPAbstractItem
8171 \brief The abstract base class for all items in a plot.
8173 In QCustomPlot, items are supplemental graphical elements that are neither plottables
8174 (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
8175 plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
8176 specific item has at least one QCPItemPosition member which controls the positioning. Some items
8177 are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
8178 example, QCPItemRect has \a topLeft and \a bottomRight).
8180 This abstract base class defines a very basic interface like visibility and clipping. Since this
8181 class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
8182 yourself to create new items.
8184 The built-in items are:
8185 <table>
8186 <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>
8187 <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>
8188 <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>
8189 <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
8190 <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
8191 <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
8192 <tr><td>QCPItemText</td><td>A text label</td></tr>
8193 <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
8194 <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>
8195 </table>
8197 \section items-clipping Clipping
8199 Items are by default clipped to the main axis rect (they are only visible inside the axis rect).
8200 To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect
8201 "setClipToAxisRect(false)".
8203 On the other hand if you want the item to be clipped to a different axis rect, specify it via
8204 \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and
8205 in principle is independent of the coordinate axes the item might be tied to via its position
8206 members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping
8207 also contains the axes used for the item positions.
8209 \section items-using Using items
8211 First you instantiate the item you want to use and add it to the plot:
8212 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1
8213 by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
8214 set the plot coordinates where the line should start/end:
8215 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2
8216 If we don't want the line to be positioned in plot coordinates but a different coordinate system,
8217 e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this:
8218 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3
8219 Then we can set the coordinates, this time in pixels:
8220 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4
8221 and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect:
8222 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5
8224 For more advanced plots, it is even possible to set different types and parent anchors per X/Y
8225 coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref
8226 QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition.
8228 \section items-subclassing Creating own items
8230 To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
8231 virtual functions, you must implement:
8232 \li \ref selectTest
8233 \li \ref draw
8235 See the documentation of those functions for what they need to do.
8237 \subsection items-positioning Allowing the item to be positioned
8239 As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
8240 have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add
8241 a public member of type QCPItemPosition like so:
8243 \code QCPItemPosition * const myPosition;\endcode
8245 the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
8246 instance it points to, can be modified, of course).
8247 The initialization of this pointer is made easy with the \ref createPosition function. Just assign
8248 the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
8249 takes a string which is the name of the position, typically this is identical to the variable name.
8250 For example, the constructor of QCPItemExample could look like this:
8252 \code
8253 QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
8254 QCPAbstractItem(parentPlot),
8255 myPosition(createPosition("myPosition"))
8257 // other constructor code
8259 \endcode
8261 \subsection items-drawing The draw function
8263 To give your item a visual representation, reimplement the \ref draw function and use the passed
8264 QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the
8265 position member(s) via \ref QCPItemPosition::pixelPoint.
8267 To optimize performance you should calculate a bounding rect first (don't forget to take the pen
8268 width into account), check whether it intersects the \ref clipRect, and only draw the item at all
8269 if this is the case.
8271 \subsection items-selection The selectTest function
8273 Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
8274 \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
8275 simpler for most items. See the documentation of \ref selectTest for what the function parameters
8276 mean and what the function should return.
8278 \subsection anchors Providing anchors
8280 Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
8281 member, e.g.
8283 \code QCPItemAnchor * const bottom;\endcode
8285 and create it in the constructor with the \ref createAnchor function, assigning it a name and an
8286 anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
8287 Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
8288 provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
8289 anchorId) function.
8291 In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
8292 position when anything attached to the anchor needs to know the coordinates.
8295 /* start of documentation of inline functions */
8297 /*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
8299 Returns all positions of the item in a list.
8301 \see anchors, position
8304 /*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
8306 Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
8307 also an anchor, the list will also contain the positions of this item.
8309 \see positions, anchor
8312 /* end of documentation of inline functions */
8313 /* start documentation of pure virtual functions */
8315 /*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
8316 \internal
8318 Draws this item with the provided \a painter.
8320 The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
8321 function is called. The clipRect depends on the clipping settings defined by \ref
8322 setClipToAxisRect and \ref setClipAxisRect.
8325 /* end documentation of pure virtual functions */
8326 /* start documentation of signals */
8328 /*! \fn void QCPAbstractItem::selectionChanged(bool selected)
8329 This signal is emitted when the selection state of this item has changed, either by user interaction
8330 or by a direct call to \ref setSelected.
8333 /* end documentation of signals */
8336 Base class constructor which initializes base class members.
8338 QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
8339 QCPLayerable(parentPlot),
8340 mClipToAxisRect(false),
8341 mSelectable(true),
8342 mSelected(false)
8344 QList<QCPAxisRect*> rects = parentPlot->axisRects();
8345 if (rects.size() > 0)
8347 setClipToAxisRect(true);
8348 setClipAxisRect(rects.first());
8352 QCPAbstractItem::~QCPAbstractItem()
8354 // don't delete mPositions because every position is also an anchor and thus in mAnchors
8355 qDeleteAll(mAnchors);
8358 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8359 QCPAxisRect *QCPAbstractItem::clipAxisRect() const
8361 return mClipAxisRect.data();
8365 Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the
8366 entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect.
8368 \see setClipAxisRect
8370 void QCPAbstractItem::setClipToAxisRect(bool clip)
8372 mClipToAxisRect = clip;
8373 if (mClipToAxisRect)
8374 setParentLayerable(mClipAxisRect.data());
8378 Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref
8379 setClipToAxisRect is set to true.
8381 \see setClipToAxisRect
8383 void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)
8385 mClipAxisRect = rect;
8386 if (mClipToAxisRect)
8387 setParentLayerable(mClipAxisRect.data());
8391 Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
8392 (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
8394 However, even when \a selectable was set to false, it is possible to set the selection manually,
8395 by calling \ref setSelected.
8397 \see QCustomPlot::setInteractions, setSelected
8399 void QCPAbstractItem::setSelectable(bool selectable)
8401 if (mSelectable != selectable)
8403 mSelectable = selectable;
8404 emit selectableChanged(mSelectable);
8409 Sets whether this item is selected or not. When selected, it might use a different visual
8410 appearance (e.g. pen and brush), this depends on the specific item though.
8412 The entire selection mechanism for items is handled automatically when \ref
8413 QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this
8414 function when you wish to change the selection state manually.
8416 This function can change the selection state even when \ref setSelectable was set to false.
8418 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
8420 \see setSelectable, selectTest
8422 void QCPAbstractItem::setSelected(bool selected)
8424 if (mSelected != selected)
8426 mSelected = selected;
8427 emit selectionChanged(mSelected);
8432 Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
8433 that name, returns 0.
8435 This function provides an alternative way to access item positions. Normally, you access
8436 positions direcly by their member pointers (which typically have the same variable name as \a
8437 name).
8439 \see positions, anchor
8441 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8443 for (int i=0; i<mPositions.size(); ++i)
8445 if (mPositions.at(i)->name() == name)
8446 return mPositions.at(i);
8448 qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8449 return 0;
8453 Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
8454 that name, returns 0.
8456 This function provides an alternative way to access item anchors. Normally, you access
8457 anchors direcly by their member pointers (which typically have the same variable name as \a
8458 name).
8460 \see anchors, position
8462 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8464 for (int i=0; i<mAnchors.size(); ++i)
8466 if (mAnchors.at(i)->name() == name)
8467 return mAnchors.at(i);
8469 qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8470 return 0;
8474 Returns whether this item has an anchor with the specified \a name.
8476 Note that you can check for positions with this function, too. This is because every position is
8477 also an anchor (QCPItemPosition inherits from QCPItemAnchor).
8479 \see anchor, position
8481 bool QCPAbstractItem::hasAnchor(const QString &name) const
8483 for (int i=0; i<mAnchors.size(); ++i)
8485 if (mAnchors.at(i)->name() == name)
8486 return true;
8488 return false;
8491 /*! \internal
8493 Returns the rect the visual representation of this item is clipped to. This depends on the
8494 current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect.
8496 If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
8498 \see draw
8500 QRect QCPAbstractItem::clipRect() const
8502 if (mClipToAxisRect && mClipAxisRect)
8503 return mClipAxisRect.data()->rect();
8504 else
8505 return mParentPlot->viewport();
8508 /*! \internal
8510 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8511 before drawing item lines.
8513 This is the antialiasing state the painter passed to the \ref draw method is in by default.
8515 This function takes into account the local setting of the antialiasing flag as well as the
8516 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
8517 QCustomPlot::setNotAntialiasedElements.
8519 \see setAntialiased
8521 void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
8523 applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
8526 /*! \internal
8528 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
8529 end.
8531 This function may be used to help with the implementation of the \ref selectTest function for
8532 specific items.
8534 \note This function is identical to QCPAbstractPlottable::distSqrToLine
8536 \see rectSelectTest
8538 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8540 QVector2D a(start);
8541 QVector2D b(end);
8542 QVector2D p(point);
8543 QVector2D v(b-a);
8545 double vLengthSqr = v.lengthSquared();
8546 if (!qFuzzyIsNull(vLengthSqr))
8548 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8549 if (mu < 0)
8550 return (a-p).lengthSquared();
8551 else if (mu > 1)
8552 return (b-p).lengthSquared();
8553 else
8554 return ((a + mu*v)-p).lengthSquared();
8555 } else
8556 return (a-p).lengthSquared();
8559 /*! \internal
8561 A convenience function which returns the selectTest value for a specified \a rect and a specified
8562 click position \a pos. \a filledRect defines whether a click inside the rect should also be
8563 considered a hit or whether only the rect border is sensitive to hits.
8565 This function may be used to help with the implementation of the \ref selectTest function for
8566 specific items.
8568 For example, if your item consists of four rects, call this function four times, once for each
8569 rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
8570 values.
8572 \see distSqrToLine
8574 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8576 double result = -1;
8578 // distance to border:
8579 QList<QLineF> lines;
8580 lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8581 << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8582 double minDistSqr = std::numeric_limits<double>::max();
8583 for (int i=0; i<lines.size(); ++i)
8585 double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8586 if (distSqr < minDistSqr)
8587 minDistSqr = distSqr;
8589 result = qSqrt(minDistSqr);
8591 // filled rect, allow click inside to count as hit:
8592 if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8594 if (rect.contains(pos))
8595 result = mParentPlot->selectionTolerance()*0.99;
8597 return result;
8600 /*! \internal
8602 Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
8603 item subclasses if they want to provide anchors (QCPItemAnchor).
8605 For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
8606 ids and returns the respective pixel points of the specified anchor.
8608 \see createAnchor
8610 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8612 qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8613 return QPointF();
8616 /*! \internal
8618 Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
8619 \a name must be a unique string that is usually identical to the variable name of the position
8620 member (This is needed to provide the name-based \ref position access to positions).
8622 Don't delete positions created by this function manually, as the item will take care of it.
8624 Use this function in the constructor (initialization list) of the specific item subclass to
8625 create each position member. Don't create QCPItemPositions with \b new yourself, because they
8626 won't be registered with the item properly.
8628 \see createAnchor
8630 QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
8632 if (hasAnchor(name))
8633 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8634 QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8635 mPositions.append(newPosition);
8636 mAnchors.append(newPosition); // every position is also an anchor
8637 newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8638 newPosition->setType(QCPItemPosition::ptPlotCoords);
8639 if (mParentPlot->axisRect())
8640 newPosition->setAxisRect(mParentPlot->axisRect());
8641 newPosition->setCoords(0, 0);
8642 return newPosition;
8645 /*! \internal
8647 Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
8648 \a name must be a unique string that is usually identical to the variable name of the anchor
8649 member (This is needed to provide the name based \ref anchor access to anchors).
8651 The \a anchorId must be a number identifying the created anchor. It is recommended to create an
8652 enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
8653 to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
8654 the correct pixel coordinates for the passed anchor id.
8656 Don't delete anchors created by this function manually, as the item will take care of it.
8658 Use this function in the constructor (initialization list) of the specific item subclass to
8659 create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
8660 won't be registered with the item properly.
8662 \see createPosition
8664 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8666 if (hasAnchor(name))
8667 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8668 QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8669 mAnchors.append(newAnchor);
8670 return newAnchor;
8673 /* inherits documentation from base class */
8674 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8676 Q_UNUSED(event)
8677 Q_UNUSED(details)
8678 if (mSelectable)
8680 bool selBefore = mSelected;
8681 setSelected(additive ? !mSelected : true);
8682 if (selectionStateChanged)
8683 *selectionStateChanged = mSelected != selBefore;
8687 /* inherits documentation from base class */
8688 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8690 if (mSelectable)
8692 bool selBefore = mSelected;
8693 setSelected(false);
8694 if (selectionStateChanged)
8695 *selectionStateChanged = mSelected != selBefore;
8699 /* inherits documentation from base class */
8700 QCP::Interaction QCPAbstractItem::selectionCategory() const
8702 return QCP::iSelectItems;
8706 /*! \file */
8710 ////////////////////////////////////////////////////////////////////////////////////////////////////
8711 //////////////////// QCustomPlot
8712 ////////////////////////////////////////////////////////////////////////////////////////////////////
8714 /*! \class QCustomPlot
8716 \brief The central class of the library. This is the QWidget which displays the plot and
8717 interacts with the user.
8719 For tutorials on how to use QCustomPlot, see the website\n
8720 http://www.qcustomplot.com/
8723 /* start of documentation of inline functions */
8725 /*! \fn QRect QCustomPlot::viewport() const
8727 Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
8728 drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
8729 plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
8730 (0, 0) and size of the QCustomPlot widget.
8732 Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically
8733 an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger
8734 and contains also the axes themselves, their tick numbers, their labels, the plot title etc.
8736 Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport is temporarily
8737 modified to allow saving plots with sizes independent of the current widget size.
8740 /*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const
8742 Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just
8743 one cell with the main QCPAxisRect inside.
8746 /* end of documentation of inline functions */
8747 /* start of documentation of signals */
8749 /*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
8751 This signal is emitted when the QCustomPlot receives a mouse double click event.
8754 /*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
8756 This signal is emitted when the QCustomPlot receives a mouse press event.
8758 It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8759 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8760 QCPAxisRect::setRangeDragAxes.
8763 /*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
8765 This signal is emitted when the QCustomPlot receives a mouse move event.
8767 It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8768 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8769 QCPAxisRect::setRangeDragAxes.
8771 \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here,
8772 because the dragging starting point was saved the moment the mouse was pressed. Thus it only has
8773 a meaning for the range drag axes that were set at that moment. If you want to change the drag
8774 axes, consider doing this in the \ref mousePress signal instead.
8777 /*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
8779 This signal is emitted when the QCustomPlot receives a mouse release event.
8781 It is emitted before QCustomPlot handles any other mechanisms like object selection. So a
8782 slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or
8783 \ref QCPAbstractPlottable::setSelectable.
8786 /*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
8788 This signal is emitted when the QCustomPlot receives a mouse wheel event.
8790 It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot
8791 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref
8792 QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor.
8795 /*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8797 This signal is emitted when a plottable is clicked.
8799 \a event is the mouse event that caused the click and \a plottable is the plottable that received
8800 the click.
8802 \see plottableDoubleClick
8805 /*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8807 This signal is emitted when a plottable is double clicked.
8809 \a event is the mouse event that caused the click and \a plottable is the plottable that received
8810 the click.
8812 \see plottableClick
8815 /*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
8817 This signal is emitted when an item is clicked.
8819 \a event is the mouse event that caused the click and \a item is the item that received the
8820 click.
8822 \see itemDoubleClick
8825 /*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
8827 This signal is emitted when an item is double clicked.
8829 \a event is the mouse event that caused the click and \a item is the item that received the
8830 click.
8832 \see itemClick
8835 /*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8837 This signal is emitted when an axis is clicked.
8839 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8840 \a part indicates the part of the axis that was clicked.
8842 \see axisDoubleClick
8845 /*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8847 This signal is emitted when an axis is double clicked.
8849 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8850 \a part indicates the part of the axis that was clicked.
8852 \see axisClick
8855 /*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8857 This signal is emitted when a legend (item) is clicked.
8859 \a event is the mouse event that caused the click, \a legend is the legend that received the
8860 click and \a item is the legend item that received the click. If only the legend and no item is
8861 clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8862 two items.
8864 \see legendDoubleClick
8867 /*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8869 This signal is emitted when a legend (item) is double clicked.
8871 \a event is the mouse event that caused the click, \a legend is the legend that received the
8872 click and \a item is the legend item that received the click. If only the legend and no item is
8873 clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8874 two items.
8876 \see legendClick
8879 /*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title)
8881 This signal is emitted when a plot title is clicked.
8883 \a event is the mouse event that caused the click and \a title is the plot title that received
8884 the click.
8886 \see titleDoubleClick
8889 /*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
8891 This signal is emitted when a plot title is double clicked.
8893 \a event is the mouse event that caused the click and \a title is the plot title that received
8894 the click.
8896 \see titleClick
8899 /*! \fn void QCustomPlot::selectionChangedByUser()
8901 This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
8902 clicking. It is not emitted when the selection state of an object has changed programmatically by
8903 a direct call to setSelected() on an object or by calling \ref deselectAll.
8905 In addition to this signal, selectable objects also provide individual signals, for example
8906 QCPAxis::selectionChanged or QCPAbstractPlottable::selectionChanged. Note that those signals are
8907 emitted even if the selection state is changed programmatically.
8909 See the documentation of \ref setInteractions for details about the selection mechanism.
8911 \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends
8914 /*! \fn void QCustomPlot::beforeReplot()
8916 This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
8917 replot).
8919 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8920 replot synchronously, it won't cause an infinite recursion.
8922 \see replot, afterReplot
8925 /*! \fn void QCustomPlot::afterReplot()
8927 This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
8928 replot).
8930 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8931 replot synchronously, it won't cause an infinite recursion.
8933 \see replot, beforeReplot
8936 /* end of documentation of signals */
8937 /* start of documentation of public members */
8939 /*! \var QCPAxis *QCustomPlot::xAxis
8941 A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
8943 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8944 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8945 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8946 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8947 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8948 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8949 axis rect), the corresponding pointers become 0.
8952 /*! \var QCPAxis *QCustomPlot::yAxis
8954 A pointer to the primary y Axis (left) of the main axis rect of the plot.
8956 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8957 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8958 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8959 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8960 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8961 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8962 axis rect), the corresponding pointers become 0.
8965 /*! \var QCPAxis *QCustomPlot::xAxis2
8967 A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are
8968 invisible by default. Use QCPAxis::setVisible to change this (or use \ref
8969 QCPAxisRect::setupFullAxesBox).
8971 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8972 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8973 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8974 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8975 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8976 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8977 axis rect), the corresponding pointers become 0.
8980 /*! \var QCPAxis *QCustomPlot::yAxis2
8982 A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are
8983 invisible by default. Use QCPAxis::setVisible to change this (or use \ref
8984 QCPAxisRect::setupFullAxesBox).
8986 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8987 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8988 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8989 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8990 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8991 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8992 axis rect), the corresponding pointers become 0.
8995 /*! \var QCPLegend *QCustomPlot::legend
8997 A pointer to the default legend of the main axis rect. The legend is invisible by default. Use
8998 QCPLegend::setVisible to change this.
9000 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
9001 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
9002 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
9003 layout system\endlink to add multiple legends to the plot, use the layout system interface to
9004 access the new legend. For example, legends can be placed inside an axis rect's \ref
9005 QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If
9006 the default legend is removed due to manipulation of the layout system (e.g. by removing the main
9007 axis rect), the corresponding pointer becomes 0.
9010 /* end of documentation of public members */
9013 Constructs a QCustomPlot and sets reasonable default values.
9015 QCustomPlot::QCustomPlot(QWidget *parent) :
9016 QWidget(parent),
9017 xAxis(0),
9018 yAxis(0),
9019 xAxis2(0),
9020 yAxis2(0),
9021 legend(0),
9022 mPlotLayout(0),
9023 mAutoAddPlottableToLegend(true),
9024 mAntialiasedElements(QCP::aeNone),
9025 mNotAntialiasedElements(QCP::aeNone),
9026 mInteractions(0),
9027 mSelectionTolerance(8),
9028 mNoAntialiasingOnDrag(false),
9029 mBackgroundBrush(Qt::white, Qt::SolidPattern),
9030 mBackgroundScaled(true),
9031 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9032 mCurrentLayer(0),
9033 mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9034 mMultiSelectModifier(Qt::ControlModifier),
9035 mPaintBuffer(size()),
9036 mMouseEventElement(0),
9037 mReplotting(false)
9039 setAttribute(Qt::WA_NoMousePropagation);
9040 setAttribute(Qt::WA_OpaquePaintEvent);
9041 setMouseTracking(true);
9042 QLocale currentLocale = locale();
9043 currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9044 setLocale(currentLocale);
9046 // create initial layers:
9047 mLayers.append(new QCPLayer(this, QLatin1String("background")));
9048 mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9049 mLayers.append(new QCPLayer(this, QLatin1String("main")));
9050 mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9051 mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9052 updateLayerIndices();
9053 setCurrentLayer(QLatin1String("main"));
9055 // create initial layout, axis rect and legend:
9056 mPlotLayout = new QCPLayoutGrid;
9057 mPlotLayout->initializeParentPlot(this);
9058 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9059 mPlotLayout->setLayer(QLatin1String("main"));
9060 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9061 mPlotLayout->addElement(0, 0, defaultAxisRect);
9062 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9063 yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9064 xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9065 yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9066 legend = new QCPLegend;
9067 legend->setVisible(false);
9068 defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9069 defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9071 defaultAxisRect->setLayer(QLatin1String("background"));
9072 xAxis->setLayer(QLatin1String("axes"));
9073 yAxis->setLayer(QLatin1String("axes"));
9074 xAxis2->setLayer(QLatin1String("axes"));
9075 yAxis2->setLayer(QLatin1String("axes"));
9076 xAxis->grid()->setLayer(QLatin1String("grid"));
9077 yAxis->grid()->setLayer(QLatin1String("grid"));
9078 xAxis2->grid()->setLayer(QLatin1String("grid"));
9079 yAxis2->grid()->setLayer(QLatin1String("grid"));
9080 legend->setLayer(QLatin1String("legend"));
9082 setViewport(rect()); // needs to be called after mPlotLayout has been created
9084 replot();
9087 QCustomPlot::~QCustomPlot()
9089 clearPlottables();
9090 clearItems();
9092 if (mPlotLayout)
9094 delete mPlotLayout;
9095 mPlotLayout = 0;
9098 mCurrentLayer = 0;
9099 qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9100 mLayers.clear();
9104 Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement.
9106 This overrides the antialiasing settings for whole element groups, normally controlled with the
9107 \a setAntialiasing function on the individual elements. If an element is neither specified in
9108 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9109 each individual element instance is used.
9111 For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
9112 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9115 if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is
9116 removed from there.
9118 \see setNotAntialiasedElements
9120 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9122 mAntialiasedElements = antialiasedElements;
9124 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9125 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9126 mNotAntialiasedElements |= ~mAntialiasedElements;
9130 Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
9132 See \ref setAntialiasedElements for details.
9134 \see setNotAntialiasedElement
9136 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9138 if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9139 mAntialiasedElements &= ~antialiasedElement;
9140 else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9141 mAntialiasedElements |= antialiasedElement;
9143 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9144 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9145 mNotAntialiasedElements |= ~mAntialiasedElements;
9149 Sets which elements are forcibly drawn not antialiased as an \a or combination of
9150 QCP::AntialiasedElement.
9152 This overrides the antialiasing settings for whole element groups, normally controlled with the
9153 \a setAntialiasing function on the individual elements. If an element is neither specified in
9154 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9155 each individual element instance is used.
9157 For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
9158 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9161 if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
9162 removed from there.
9164 \see setAntialiasedElements
9166 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
9168 mNotAntialiasedElements = notAntialiasedElements;
9170 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9171 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9172 mAntialiasedElements |= ~mNotAntialiasedElements;
9176 Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
9178 See \ref setNotAntialiasedElements for details.
9180 \see setAntialiasedElement
9182 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9184 if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9185 mNotAntialiasedElements &= ~notAntialiasedElement;
9186 else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9187 mNotAntialiasedElements |= notAntialiasedElement;
9189 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9190 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9191 mAntialiasedElements |= ~mNotAntialiasedElements;
9195 If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
9196 plottable to the legend (QCustomPlot::legend).
9198 \see addPlottable, addGraph, QCPLegend::addItem
9200 void QCustomPlot::setAutoAddPlottableToLegend(bool on)
9202 mAutoAddPlottableToLegend = on;
9206 Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction
9207 enums. There are the following types of interactions:
9209 <b>Axis range manipulation</b> is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the
9210 respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
9211 For details how to control which axes the user may drag/zoom and in what orientations, see \ref
9212 QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes,
9213 \ref QCPAxisRect::setRangeZoomAxes.
9215 <b>Plottable selection</b> is controlled by \ref QCP::iSelectPlottables. If \ref QCP::iSelectPlottables is
9216 set, the user may select plottables (graphs, curves, bars,...) by clicking on them or in their
9217 vicinity (\ref setSelectionTolerance). Whether the user can actually select a plottable can
9218 further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the specific
9219 plottable. To find out whether a specific plottable is selected, call
9220 QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
9221 \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
9222 function \ref selectedGraphs.
9224 <b>Item selection</b> is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user
9225 may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find
9226 out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of
9227 all currently selected items, call \ref selectedItems.
9229 <b>Axis selection</b> is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user
9230 may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
9231 labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for
9232 each axis. To retrieve a list of all axes that currently contain selected parts, call \ref
9233 selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts().
9235 <b>Legend selection</b> is controlled with \ref QCP::iSelectLegend. If this is set, the user may
9236 select the legend itself or individual items by clicking on them. What parts exactly are
9237 selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the
9238 legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To
9239 find out which child items are selected, call \ref QCPLegend::selectedItems.
9241 <b>All other selectable elements</b> The selection of all other selectable objects (e.g.
9242 QCPPlotTitle, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the
9243 user may select those objects by clicking on them. To find out which are currently selected, you
9244 need to check their selected state explicitly.
9246 If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
9247 emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
9248 their selection state has changed, i.e. not only by user interaction.
9250 To allow multiple objects to be selected by holding the selection modifier (\ref
9251 setMultiSelectModifier), set the flag \ref QCP::iMultiSelect.
9253 \note In addition to the selection mechanism presented here, QCustomPlot always emits
9254 corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
9255 \ref plottableDoubleClick for example.
9257 \see setInteraction, setSelectionTolerance
9259 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9261 mInteractions = interactions;
9265 Sets the single \a interaction of this QCustomPlot to \a enabled.
9267 For details about the interaction system, see \ref setInteractions.
9269 \see setInteractions
9271 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9273 if (!enabled && mInteractions.testFlag(interaction))
9274 mInteractions &= ~interaction;
9275 else if (enabled && !mInteractions.testFlag(interaction))
9276 mInteractions |= interaction;
9280 Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or
9281 not.
9283 If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a
9284 potential selection when the minimum distance between the click position and the graph line is
9285 smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
9286 directly inside the area and ignore this selection tolerance. In other words, it only has meaning
9287 for parts of objects that are too thin to exactly hit with a click and thus need such a
9288 tolerance.
9290 \see setInteractions, QCPLayerable::selectTest
9292 void QCustomPlot::setSelectionTolerance(int pixels)
9294 mSelectionTolerance = pixels;
9298 Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes
9299 ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves
9300 performance during dragging. Thus it creates a more responsive user experience. As soon as the
9301 user stops dragging, the last replot is done with normal antialiasing, to restore high image
9302 quality.
9304 \see setAntialiasedElements, setNotAntialiasedElements
9306 void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
9308 mNoAntialiasingOnDrag = enabled;
9312 Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint.
9314 \see setPlottingHint
9316 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9318 mPlottingHints = hints;
9322 Sets the specified plotting \a hint to \a enabled.
9324 \see setPlottingHints
9326 void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
9328 QCP::PlottingHints newHints = mPlottingHints;
9329 if (!enabled)
9330 newHints &= ~hint;
9331 else
9332 newHints |= hint;
9334 if (newHints != mPlottingHints)
9335 setPlottingHints(newHints);
9339 Sets the keyboard modifier that will be recognized as multi-select-modifier.
9341 If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
9342 by clicking on them one after the other while holding down \a modifier.
9344 By default the multi-select-modifier is set to Qt::ControlModifier.
9346 \see setInteractions
9348 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9350 mMultiSelectModifier = modifier;
9354 Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout
9355 (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect.
9357 This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref
9358 savePdf, etc. by temporarily changing the viewport size.
9360 void QCustomPlot::setViewport(const QRect &rect)
9362 mViewport = rect;
9363 if (mPlotLayout)
9364 mPlotLayout->setOuterRect(mViewport);
9368 Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn
9369 below all other objects in the plot.
9371 For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be
9372 enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is
9373 preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
9374 consider using the overloaded version of this function.
9376 If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will
9377 first be filled with that brush, before drawing the background pixmap. This can be useful for
9378 background pixmaps with translucent areas.
9380 \see setBackgroundScaled, setBackgroundScaledMode
9382 void QCustomPlot::setBackground(const QPixmap &pm)
9384 mBackgroundPixmap = pm;
9385 mScaledBackgroundPixmap = QPixmap();
9389 Sets the background brush of the viewport (see \ref setViewport).
9391 Before drawing everything else, the background is filled with \a brush. If a background pixmap
9392 was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport
9393 before the background pixmap is drawn. This can be useful for background pixmaps with translucent
9394 areas.
9396 Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be
9397 useful for exporting to image formats which support transparency, e.g. \ref savePng.
9399 \see setBackgroundScaled, setBackgroundScaledMode
9401 void QCustomPlot::setBackground(const QBrush &brush)
9403 mBackgroundBrush = brush;
9406 /*! \overload
9408 Allows setting the background pixmap of the viewport, whether it shall be scaled and how it
9409 shall be scaled in one call.
9411 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
9413 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9415 mBackgroundPixmap = pm;
9416 mScaledBackgroundPixmap = QPixmap();
9417 mBackgroundScaled = scaled;
9418 mBackgroundScaledMode = mode;
9422 Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is
9423 set to true, control whether and how the aspect ratio of the original pixmap is preserved with
9424 \ref setBackgroundScaledMode.
9426 Note that the scaled version of the original pixmap is buffered, so there is no performance
9427 penalty on replots. (Except when the viewport dimensions are changed continuously.)
9429 \see setBackground, setBackgroundScaledMode
9431 void QCustomPlot::setBackgroundScaled(bool scaled)
9433 mBackgroundScaled = scaled;
9437 If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this
9438 function to define whether and how the aspect ratio of the original pixmap is preserved.
9440 \see setBackground, setBackgroundScaled
9442 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9444 mBackgroundScaledMode = mode;
9448 Returns the plottable with \a index. If the index is invalid, returns 0.
9450 There is an overloaded version of this function with no parameter which returns the last added
9451 plottable, see QCustomPlot::plottable()
9453 \see plottableCount, addPlottable
9455 QCPAbstractPlottable *QCustomPlot::plottable(int index)
9457 if (index >= 0 && index < mPlottables.size())
9459 return mPlottables.at(index);
9460 } else
9462 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9463 return 0;
9467 /*! \overload
9469 Returns the last plottable that was added with \ref addPlottable. If there are no plottables in
9470 the plot, returns 0.
9472 \see plottableCount, addPlottable
9474 QCPAbstractPlottable *QCustomPlot::plottable()
9476 if (!mPlottables.isEmpty())
9478 return mPlottables.last();
9479 } else
9480 return 0;
9484 Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to
9485 the legend (QCustomPlot::legend). QCustomPlot takes ownership of the plottable.
9487 Returns true on success, i.e. when \a plottable isn't already in the plot and the parent plot of
9488 \a plottable is this QCustomPlot (the latter is controlled by what axes were passed in the
9489 plottable's constructor).
9491 \see plottable, plottableCount, removePlottable, clearPlottables
9493 bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
9495 if (mPlottables.contains(plottable))
9497 qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9498 return false;
9500 if (plottable->parentPlot() != this)
9502 qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9503 return false;
9506 mPlottables.append(plottable);
9507 // possibly add plottable to legend:
9508 if (mAutoAddPlottableToLegend)
9509 plottable->addToLegend();
9510 // special handling for QCPGraphs to maintain the simple graph interface:
9511 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9512 mGraphs.append(graph);
9513 if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9514 plottable->setLayer(currentLayer());
9515 return true;
9519 Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend).
9521 Returns true on success.
9523 \see addPlottable, clearPlottables
9525 bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
9527 if (!mPlottables.contains(plottable))
9529 qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9530 return false;
9533 // remove plottable from legend:
9534 plottable->removeFromLegend();
9535 // special handling for QCPGraphs to maintain the simple graph interface:
9536 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9537 mGraphs.removeOne(graph);
9538 // remove plottable:
9539 delete plottable;
9540 mPlottables.removeOne(plottable);
9541 return true;
9544 /*! \overload
9546 Removes the plottable by its \a index.
9548 bool QCustomPlot::removePlottable(int index)
9550 if (index >= 0 && index < mPlottables.size())
9551 return removePlottable(mPlottables[index]);
9552 else
9554 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9555 return false;
9560 Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
9562 Returns the number of plottables removed.
9564 \see removePlottable
9566 int QCustomPlot::clearPlottables()
9568 int c = mPlottables.size();
9569 for (int i=c-1; i >= 0; --i)
9570 removePlottable(mPlottables[i]);
9571 return c;
9575 Returns the number of currently existing plottables in the plot
9577 \see plottable, addPlottable
9579 int QCustomPlot::plottableCount() const
9581 return mPlottables.size();
9585 Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
9587 There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
9589 \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9591 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9593 QList<QCPAbstractPlottable*> result;
9594 foreach (QCPAbstractPlottable *plottable, mPlottables)
9596 if (plottable->selected())
9597 result.append(plottable);
9599 return result;
9603 Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
9604 (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple
9605 plottables come into consideration, the one closest to \a pos is returned.
9607 If \a onlySelectable is true, only plottables that are selectable
9608 (QCPAbstractPlottable::setSelectable) are considered.
9610 If there is no plottable at \a pos, the return value is 0.
9612 \see itemAt, layoutElementAt
9614 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9616 QCPAbstractPlottable *resultPlottable = 0;
9617 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9619 foreach (QCPAbstractPlottable *plottable, mPlottables)
9621 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
9622 continue;
9623 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
9625 double currentDistance = plottable->selectTest(pos, false);
9626 if (currentDistance >= 0 && currentDistance < resultDistance)
9628 resultPlottable = plottable;
9629 resultDistance = currentDistance;
9634 return resultPlottable;
9638 Returns whether this QCustomPlot instance contains the \a plottable.
9640 \see addPlottable
9642 bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
9644 return mPlottables.contains(plottable);
9648 Returns the graph with \a index. If the index is invalid, returns 0.
9650 There is an overloaded version of this function with no parameter which returns the last created
9651 graph, see QCustomPlot::graph()
9653 \see graphCount, addGraph
9655 QCPGraph *QCustomPlot::graph(int index) const
9657 if (index >= 0 && index < mGraphs.size())
9659 return mGraphs.at(index);
9660 } else
9662 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9663 return 0;
9667 /*! \overload
9669 Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
9670 returns 0.
9672 \see graphCount, addGraph
9674 QCPGraph *QCustomPlot::graph() const
9676 if (!mGraphs.isEmpty())
9678 return mGraphs.last();
9679 } else
9680 return 0;
9684 Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
9685 bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a
9686 keyAxis and \a valueAxis must reside in this QCustomPlot.
9688 \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
9689 "y") for the graph.
9691 Returns a pointer to the newly created graph, or 0 if adding the graph failed.
9693 \see graph, graphCount, removeGraph, clearGraphs
9695 QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
9697 if (!keyAxis) keyAxis = xAxis;
9698 if (!valueAxis) valueAxis = yAxis;
9699 if (!keyAxis || !valueAxis)
9701 qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9702 return 0;
9704 if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9706 qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9707 return 0;
9710 QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9711 if (addPlottable(newGraph))
9713 newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9714 return newGraph;
9715 } else
9717 delete newGraph;
9718 return 0;
9723 Removes the specified \a graph from the plot and, if necessary, from the QCustomPlot::legend. If
9724 any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
9725 property of those graphs is reset to zero (no channel fill).
9727 Returns true on success.
9729 \see clearGraphs
9731 bool QCustomPlot::removeGraph(QCPGraph *graph)
9733 return removePlottable(graph);
9736 /*! \overload
9738 Removes the graph by its \a index.
9740 bool QCustomPlot::removeGraph(int index)
9742 if (index >= 0 && index < mGraphs.size())
9743 return removeGraph(mGraphs[index]);
9744 else
9745 return false;
9749 Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
9751 Returns the number of graphs removed.
9753 \see removeGraph
9755 int QCustomPlot::clearGraphs()
9757 int c = mGraphs.size();
9758 for (int i=c-1; i >= 0; --i)
9759 removeGraph(mGraphs[i]);
9760 return c;
9764 Returns the number of currently existing graphs in the plot
9766 \see graph, addGraph
9768 int QCustomPlot::graphCount() const
9770 return mGraphs.size();
9774 Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
9776 If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars,
9777 etc., use \ref selectedPlottables.
9779 \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9781 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9783 QList<QCPGraph*> result;
9784 foreach (QCPGraph *graph, mGraphs)
9786 if (graph->selected())
9787 result.append(graph);
9789 return result;
9793 Returns the item with \a index. If the index is invalid, returns 0.
9795 There is an overloaded version of this function with no parameter which returns the last added
9796 item, see QCustomPlot::item()
9798 \see itemCount, addItem
9800 QCPAbstractItem *QCustomPlot::item(int index) const
9802 if (index >= 0 && index < mItems.size())
9804 return mItems.at(index);
9805 } else
9807 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9808 return 0;
9812 /*! \overload
9814 Returns the last item, that was added with \ref addItem. If there are no items in the plot,
9815 returns 0.
9817 \see itemCount, addItem
9819 QCPAbstractItem *QCustomPlot::item() const
9821 if (!mItems.isEmpty())
9823 return mItems.last();
9824 } else
9825 return 0;
9829 Adds the specified item to the plot. QCustomPlot takes ownership of the item.
9831 Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a
9832 item is this QCustomPlot.
9834 \see item, itemCount, removeItem, clearItems
9836 bool QCustomPlot::addItem(QCPAbstractItem *item)
9838 if (!mItems.contains(item) && item->parentPlot() == this)
9840 mItems.append(item);
9841 return true;
9842 } else
9844 qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9845 return false;
9850 Removes the specified item from the plot.
9852 Returns true on success.
9854 \see addItem, clearItems
9856 bool QCustomPlot::removeItem(QCPAbstractItem *item)
9858 if (mItems.contains(item))
9860 delete item;
9861 mItems.removeOne(item);
9862 return true;
9863 } else
9865 qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9866 return false;
9870 /*! \overload
9872 Removes the item by its \a index.
9874 bool QCustomPlot::removeItem(int index)
9876 if (index >= 0 && index < mItems.size())
9877 return removeItem(mItems[index]);
9878 else
9880 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9881 return false;
9886 Removes all items from the plot.
9888 Returns the number of items removed.
9890 \see removeItem
9892 int QCustomPlot::clearItems()
9894 int c = mItems.size();
9895 for (int i=c-1; i >= 0; --i)
9896 removeItem(mItems[i]);
9897 return c;
9901 Returns the number of currently existing items in the plot
9903 \see item, addItem
9905 int QCustomPlot::itemCount() const
9907 return mItems.size();
9911 Returns a list of the selected items. If no items are currently selected, the list is empty.
9913 \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
9915 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9917 QList<QCPAbstractItem*> result;
9918 foreach (QCPAbstractItem *item, mItems)
9920 if (item->selected())
9921 result.append(item);
9923 return result;
9927 Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
9928 QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
9929 setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
9930 returned.
9932 If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
9933 considered.
9935 If there is no item at \a pos, the return value is 0.
9937 \see plottableAt, layoutElementAt
9939 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9941 QCPAbstractItem *resultItem = 0;
9942 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9944 foreach (QCPAbstractItem *item, mItems)
9946 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
9947 continue;
9948 if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9950 double currentDistance = item->selectTest(pos, false);
9951 if (currentDistance >= 0 && currentDistance < resultDistance)
9953 resultItem = item;
9954 resultDistance = currentDistance;
9959 return resultItem;
9963 Returns whether this QCustomPlot contains the \a item.
9965 \see addItem
9967 bool QCustomPlot::hasItem(QCPAbstractItem *item) const
9969 return mItems.contains(item);
9973 Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is
9974 returned.
9976 Layer names are case-sensitive.
9978 \see addLayer, moveLayer, removeLayer
9980 QCPLayer *QCustomPlot::layer(const QString &name) const
9982 foreach (QCPLayer *layer, mLayers)
9984 if (layer->name() == name)
9985 return layer;
9987 return 0;
9990 /*! \overload
9992 Returns the layer by \a index. If the index is invalid, 0 is returned.
9994 \see addLayer, moveLayer, removeLayer
9996 QCPLayer *QCustomPlot::layer(int index) const
9998 if (index >= 0 && index < mLayers.size())
10000 return mLayers.at(index);
10001 } else
10003 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10004 return 0;
10009 Returns the layer that is set as current layer (see \ref setCurrentLayer).
10011 QCPLayer *QCustomPlot::currentLayer() const
10013 return mCurrentLayer;
10017 Sets the layer with the specified \a name to be the current layer. All layerables (\ref
10018 QCPLayerable), e.g. plottables and items, are created on the current layer.
10020 Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
10022 Layer names are case-sensitive.
10024 \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer
10026 bool QCustomPlot::setCurrentLayer(const QString &name)
10028 if (QCPLayer *newCurrentLayer = layer(name))
10030 return setCurrentLayer(newCurrentLayer);
10031 } else
10033 qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10034 return false;
10038 /*! \overload
10040 Sets the provided \a layer to be the current layer.
10042 Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
10044 \see addLayer, moveLayer, removeLayer
10046 bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
10048 if (!mLayers.contains(layer))
10050 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10051 return false;
10054 mCurrentLayer = layer;
10055 return true;
10059 Returns the number of currently existing layers in the plot
10061 \see layer, addLayer
10063 int QCustomPlot::layerCount() const
10065 return mLayers.size();
10069 Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which
10070 must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer.
10072 Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
10073 valid layer inside this QCustomPlot.
10075 If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
10077 For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
10079 \see layer, moveLayer, removeLayer
10081 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10083 if (!otherLayer)
10084 otherLayer = mLayers.last();
10085 if (!mLayers.contains(otherLayer))
10087 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10088 return false;
10090 if (layer(name))
10092 qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10093 return false;
10096 QCPLayer *newLayer = new QCPLayer(this, name);
10097 mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10098 updateLayerIndices();
10099 return true;
10103 Removes the specified \a layer and returns true on success.
10105 All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
10106 \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
10107 cases, the total rendering order of all layerables in the QCustomPlot is preserved.
10109 If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
10110 layer) becomes the new current layer.
10112 It is not possible to remove the last layer of the plot.
10114 \see layer, addLayer, moveLayer
10116 bool QCustomPlot::removeLayer(QCPLayer *layer)
10118 if (!mLayers.contains(layer))
10120 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10121 return false;
10123 if (mLayers.size() < 2)
10125 qDebug() << Q_FUNC_INFO << "can't remove last layer";
10126 return false;
10129 // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10130 int removedIndex = layer->index();
10131 bool isFirstLayer = removedIndex==0;
10132 QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10133 QList<QCPLayerable*> children = layer->children();
10134 if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10136 for (int i=children.size()-1; i>=0; --i)
10137 children.at(i)->moveToLayer(targetLayer, true);
10138 } else // append normally
10140 for (int i=0; i<children.size(); ++i)
10141 children.at(i)->moveToLayer(targetLayer, false);
10143 // if removed layer is current layer, change current layer to layer below/above:
10144 if (layer == mCurrentLayer)
10145 setCurrentLayer(targetLayer);
10146 // remove layer:
10147 delete layer;
10148 mLayers.removeOne(layer);
10149 updateLayerIndices();
10150 return true;
10154 Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or
10155 below is controlled with \a insertMode.
10157 Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
10158 QCustomPlot.
10160 \see layer, addLayer, moveLayer
10162 bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10164 if (!mLayers.contains(layer))
10166 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10167 return false;
10169 if (!mLayers.contains(otherLayer))
10171 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10172 return false;
10175 mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10176 updateLayerIndices();
10177 return true;
10181 Returns the number of axis rects in the plot.
10183 All axis rects can be accessed via QCustomPlot::axisRect().
10185 Initially, only one axis rect exists in the plot.
10187 \see axisRect, axisRects
10189 int QCustomPlot::axisRectCount() const
10191 return axisRects().size();
10195 Returns the axis rect with \a index.
10197 Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were
10198 added, all of them may be accessed with this function in a linear fashion (even when they are
10199 nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).
10201 \see axisRectCount, axisRects
10203 QCPAxisRect *QCustomPlot::axisRect(int index) const
10205 const QList<QCPAxisRect*> rectList = axisRects();
10206 if (index >= 0 && index < rectList.size())
10208 return rectList.at(index);
10209 } else
10211 qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10212 return 0;
10217 Returns all axis rects in the plot.
10219 \see axisRectCount, axisRect
10221 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10223 QList<QCPAxisRect*> result;
10224 QStack<QCPLayoutElement*> elementStack;
10225 if (mPlotLayout)
10226 elementStack.push(mPlotLayout);
10228 while (!elementStack.isEmpty())
10230 foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10232 if (element)
10234 elementStack.push(element);
10235 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10236 result.append(ar);
10241 return result;
10245 Returns the layout element at pixel position \a pos. If there is no element at that position,
10246 returns 0.
10248 Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on
10249 any of its parent elements is set to false, it will not be considered.
10251 \see itemAt, plottableAt
10253 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10255 QCPLayoutElement *currentElement = mPlotLayout;
10256 bool searchSubElements = true;
10257 while (searchSubElements && currentElement)
10259 searchSubElements = false;
10260 foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10262 if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10264 currentElement = subElement;
10265 searchSubElements = true;
10266 break;
10270 return currentElement;
10274 Returns the axes that currently have selected parts, i.e. whose selection state is not \ref
10275 QCPAxis::spNone.
10277 \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts,
10278 QCPAxis::setSelectableParts
10280 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10282 QList<QCPAxis*> result, allAxes;
10283 foreach (QCPAxisRect *rect, axisRects())
10284 allAxes << rect->axes();
10286 foreach (QCPAxis *axis, allAxes)
10288 if (axis->selectedParts() != QCPAxis::spNone)
10289 result.append(axis);
10292 return result;
10296 Returns the legends that currently have selected parts, i.e. whose selection state is not \ref
10297 QCPLegend::spNone.
10299 \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts,
10300 QCPLegend::setSelectableParts, QCPLegend::selectedItems
10302 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10304 QList<QCPLegend*> result;
10306 QStack<QCPLayoutElement*> elementStack;
10307 if (mPlotLayout)
10308 elementStack.push(mPlotLayout);
10310 while (!elementStack.isEmpty())
10312 foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10314 if (subElement)
10316 elementStack.push(subElement);
10317 if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10319 if (leg->selectedParts() != QCPLegend::spNone)
10320 result.append(leg);
10326 return result;
10330 Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
10332 Since calling this function is not a user interaction, this does not emit the \ref
10333 selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
10334 objects were previously selected.
10336 \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
10338 void QCustomPlot::deselectAll()
10340 foreach (QCPLayer *layer, mLayers)
10342 foreach (QCPLayerable *layerable, layer->children())
10343 layerable->deselectEvent(0);
10348 Causes a complete replot into the internal buffer. Finally, update() is called, to redraw the
10349 buffer on the QCustomPlot widget surface. This is the method that must be called to make changes,
10350 for example on the axis ranges or data points of graphs, visible.
10352 Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the
10353 QCustomPlot widget and user interactions (object selection and range dragging/zooming).
10355 Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref
10356 afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two
10357 signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10358 recursion.
10360 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
10362 if (mReplotting) // incase signals loop back to replot slot
10363 return;
10364 mReplotting = true;
10365 emit beforeReplot();
10367 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10368 QCPPainter painter;
10369 painter.begin(&mPaintBuffer);
10370 if (painter.isActive())
10372 painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10373 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10374 painter.fillRect(mViewport, mBackgroundBrush);
10375 draw(&painter);
10376 painter.end();
10377 if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10378 repaint();
10379 else
10380 update();
10381 } else // might happen if QCustomPlot has width or height zero
10382 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10384 emit afterReplot();
10385 mReplotting = false;
10389 Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
10391 if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true
10392 (QCPLayerable::setVisible), will be used to rescale the axes.
10394 \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale
10396 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10398 QList<QCPAxis*> allAxes;
10399 foreach (QCPAxisRect *rect, axisRects())
10400 allAxes << rect->axes();
10402 foreach (QCPAxis *axis, allAxes)
10403 axis->rescale(onlyVisiblePlottables);
10407 Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
10408 of texts and lines will be derived from the specified \a width and \a height. This means, the
10409 output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
10410 pixel width and height. If either \a width or \a height is zero, the exported image will have the
10411 same dimensions as the QCustomPlot widget currently has.
10413 \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
10414 are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
10415 zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see the QPainter
10416 and QPen documentation.
10418 The objects of the plot will appear in the current selection state. If you don't want any
10419 selected objects to be painted in their selected look, deselect everything with \ref deselectAll
10420 before calling this function.
10422 Returns true on success.
10424 \warning
10425 \li If you plan on editing the exported PDF file with a vector graphics editor like
10426 Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
10427 (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
10428 \li If calling this function inside the constructor of the parent of the QCustomPlot widget
10429 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10430 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10431 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10432 aren't defined yet inside the constructor, so you would get an image that has strange
10433 widths/heights.
10435 \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
10436 PDF file.
10438 \note On Android systems, this method does nothing and issues an according qDebug warning
10439 message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10441 \see savePng, saveBmp, saveJpg, saveRastered
10443 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10445 bool success = false;
10446 #ifdef QT_NO_PRINTER
10447 Q_UNUSED(fileName)
10448 Q_UNUSED(noCosmeticPen)
10449 Q_UNUSED(width)
10450 Q_UNUSED(height)
10451 Q_UNUSED(pdfCreator)
10452 Q_UNUSED(pdfTitle)
10453 qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10454 #else
10455 int newWidth, newHeight;
10456 if (width == 0 || height == 0)
10458 newWidth = this->width();
10459 newHeight = this->height();
10460 } else
10462 newWidth = width;
10463 newHeight = height;
10466 QPrinter printer(QPrinter::ScreenResolution);
10467 printer.setOutputFileName(fileName);
10468 printer.setOutputFormat(QPrinter::PdfFormat);
10469 printer.setColorMode(QPrinter::Color);
10470 printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10471 printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10472 QRect oldViewport = viewport();
10473 setViewport(QRect(0, 0, newWidth, newHeight));
10474 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10475 printer.setFullPage(true);
10476 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10477 #else
10478 QPageLayout pageLayout;
10479 pageLayout.setMode(QPageLayout::FullPageMode);
10480 pageLayout.setOrientation(QPageLayout::Portrait);
10481 pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10482 pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10483 printer.setPageLayout(pageLayout);
10484 #endif
10485 QCPPainter printpainter;
10486 if (printpainter.begin(&printer))
10488 printpainter.setMode(QCPPainter::pmVectorized);
10489 printpainter.setMode(QCPPainter::pmNoCaching);
10490 printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10491 printpainter.setWindow(mViewport);
10492 if (mBackgroundBrush.style() != Qt::NoBrush &&
10493 mBackgroundBrush.color() != Qt::white &&
10494 mBackgroundBrush.color() != Qt::transparent &&
10495 mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10496 printpainter.fillRect(viewport(), mBackgroundBrush);
10497 draw(&printpainter);
10498 printpainter.end();
10499 success = true;
10501 setViewport(oldViewport);
10502 #endif // QT_NO_PRINTER
10503 return success;
10507 Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
10508 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10509 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10510 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10512 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10513 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10514 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10515 200*200 pixel resolution.
10517 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10518 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10519 QCustomPlot to place objects with sub-pixel accuracy.
10521 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10522 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10523 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10524 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10525 aren't defined yet inside the constructor, so you would get an image that has strange
10526 widths/heights.
10528 The objects of the plot will appear in the current selection state. If you don't want any selected
10529 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10530 this function.
10532 If you want the PNG to have a transparent background, call \ref setBackground(const QBrush
10533 &brush) with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving.
10535 PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10536 -1 to use the default setting.
10538 Returns true on success. If this function fails, most likely the PNG format isn't supported by
10539 the system, see Qt docs about QImageWriter::supportedImageFormats().
10541 \see savePdf, saveBmp, saveJpg, saveRastered
10543 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10545 return saveRastered(fileName, width, height, scale, "PNG", quality);
10549 Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
10550 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10551 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10552 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10554 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10555 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10556 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10557 200*200 pixel resolution.
10559 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10560 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10561 QCustomPlot to place objects with sub-pixel accuracy.
10563 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10564 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10565 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10566 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10567 aren't defined yet inside the constructor, so you would get an image that has strange
10568 widths/heights.
10570 The objects of the plot will appear in the current selection state. If you don't want any selected
10571 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10572 this function.
10574 JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10575 -1 to use the default setting.
10577 Returns true on success. If this function fails, most likely the JPG format isn't supported by
10578 the system, see Qt docs about QImageWriter::supportedImageFormats().
10580 \see savePdf, savePng, saveBmp, saveRastered
10582 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10584 return saveRastered(fileName, width, height, scale, "JPG", quality);
10588 Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
10589 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10590 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10591 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10593 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10594 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10595 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10596 200*200 pixel resolution.
10598 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10599 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10600 QCustomPlot to place objects with sub-pixel accuracy.
10602 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10603 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10604 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10605 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10606 aren't defined yet inside the constructor, so you would get an image that has strange
10607 widths/heights.
10609 The objects of the plot will appear in the current selection state. If you don't want any selected
10610 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10611 this function.
10613 Returns true on success. If this function fails, most likely the BMP format isn't supported by
10614 the system, see Qt docs about QImageWriter::supportedImageFormats().
10616 \see savePdf, savePng, saveJpg, saveRastered
10618 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10620 return saveRastered(fileName, width, height, scale, "BMP");
10623 /*! \internal
10625 Returns a minimum size hint that corresponds to the minimum size of the top level layout
10626 (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum
10627 size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot.
10628 This is especially important, when placed in a QLayout where other components try to take in as
10629 much space as possible (e.g. QMdiArea).
10631 QSize QCustomPlot::minimumSizeHint() const
10633 return mPlotLayout->minimumSizeHint();
10636 /*! \internal
10638 Returns a size hint that is the same as \ref minimumSizeHint.
10641 QSize QCustomPlot::sizeHint() const
10643 return mPlotLayout->minimumSizeHint();
10646 /*! \internal
10648 Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but
10649 draws the internal buffer on the widget surface.
10651 void QCustomPlot::paintEvent(QPaintEvent *event)
10653 Q_UNUSED(event);
10654 QPainter painter(this);
10655 painter.drawPixmap(0, 0, mPaintBuffer);
10658 /*! \internal
10660 Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
10661 the new size. The viewport (which becomes the outer rect of mPlotLayout) is resized
10662 appropriately. Finally a \ref replot is performed.
10664 void QCustomPlot::resizeEvent(QResizeEvent *event)
10666 // resize and repaint the buffer:
10667 mPaintBuffer = QPixmap(event->size());
10668 setViewport(rect());
10669 replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10672 /*! \internal
10674 Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then emits
10675 the specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref
10676 axisDoubleClick, etc.). Finally determines the affected layout element and forwards the event to
10679 \see mousePressEvent, mouseReleaseEvent
10681 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10683 emit mouseDoubleClick(event);
10685 QVariant details;
10686 QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10688 // emit specialized object double click signals:
10689 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10690 emit plottableDoubleClick(ap, event);
10691 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10692 emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10693 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10694 emit itemDoubleClick(ai, event);
10695 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10696 emit legendDoubleClick(lg, 0, event);
10697 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10698 emit legendDoubleClick(li->parentLegend(), li, event);
10699 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10700 emit titleDoubleClick(event, pt);
10702 // call double click event of affected layout element:
10703 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10704 el->mouseDoubleClickEvent(event);
10706 // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10707 if (mMouseEventElement)
10709 mMouseEventElement->mouseReleaseEvent(event);
10710 mMouseEventElement = 0;
10713 //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10716 /*! \internal
10718 Event handler for when a mouse button is pressed. Emits the mousePress signal. Then determines
10719 the affected layout element and forwards the event to it.
10721 \see mouseMoveEvent, mouseReleaseEvent
10723 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10725 emit mousePress(event);
10726 mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10728 // call event of affected layout element:
10729 mMouseEventElement = layoutElementAt(event->pos());
10730 if (mMouseEventElement)
10731 mMouseEventElement->mousePressEvent(event);
10733 QWidget::mousePressEvent(event);
10736 /*! \internal
10738 Event handler for when the cursor is moved. Emits the \ref mouseMove signal.
10740 If a layout element has mouse capture focus (a mousePressEvent happened on top of the layout
10741 element before), the mouseMoveEvent is forwarded to that element.
10743 \see mousePressEvent, mouseReleaseEvent
10745 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10747 emit mouseMove(event);
10749 // call event of affected layout element:
10750 if (mMouseEventElement)
10751 mMouseEventElement->mouseMoveEvent(event);
10753 QWidget::mouseMoveEvent(event);
10756 /*! \internal
10758 Event handler for when a mouse button is released. Emits the \ref mouseRelease signal.
10760 If the mouse was moved less than a certain threshold in any direction since the \ref
10761 mousePressEvent, it is considered a click which causes the selection mechanism (if activated via
10762 \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse
10763 click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.)
10765 If a layout element has mouse capture focus (a \ref mousePressEvent happened on top of the layout
10766 element before), the \ref mouseReleaseEvent is forwarded to that element.
10768 \see mousePressEvent, mouseMoveEvent
10770 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10772 emit mouseRelease(event);
10773 bool doReplot = false;
10775 if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10777 if (event->button() == Qt::LeftButton)
10779 // handle selection mechanism:
10780 QVariant details;
10781 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10782 bool selectionStateChanged = false;
10783 bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10784 // deselect all other layerables if not additive selection:
10785 if (!additive)
10787 foreach (QCPLayer *layer, mLayers)
10789 foreach (QCPLayerable *layerable, layer->children())
10791 if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10793 bool selChanged = false;
10794 layerable->deselectEvent(&selChanged);
10795 selectionStateChanged |= selChanged;
10800 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10802 // a layerable was actually clicked, call its selectEvent:
10803 bool selChanged = false;
10804 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10805 selectionStateChanged |= selChanged;
10807 if (selectionStateChanged)
10809 doReplot = true;
10810 emit selectionChangedByUser();
10814 // emit specialized object click signals:
10815 QVariant details;
10816 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
10817 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10818 emit plottableClick(ap, event);
10819 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10820 emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10821 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10822 emit itemClick(ai, event);
10823 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10824 emit legendClick(lg, 0, event);
10825 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10826 emit legendClick(li->parentLegend(), li, event);
10827 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10828 emit titleClick(event, pt);
10831 // call event of affected layout element:
10832 if (mMouseEventElement)
10834 mMouseEventElement->mouseReleaseEvent(event);
10835 mMouseEventElement = 0;
10838 if (doReplot || noAntialiasingOnDrag())
10839 replot();
10841 QWidget::mouseReleaseEvent(event);
10844 /*! \internal
10846 Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then
10847 determines the affected layout element and forwards the event to it.
10850 void QCustomPlot::wheelEvent(QWheelEvent *event)
10852 emit mouseWheel(event);
10854 // call event of affected layout element:
10855 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10856 el->wheelEvent(event);
10858 QWidget::wheelEvent(event);
10861 /*! \internal
10863 This is the main draw function. It draws the entire plot, including background pixmap, with the
10864 specified \a painter. Note that it does not fill the background with the background brush (as the
10865 user may specify with \ref setBackground(const QBrush &brush)), this is up to the respective
10866 functions calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter).
10868 void QCustomPlot::draw(QCPPainter *painter)
10870 // run through layout phases:
10871 mPlotLayout->update(QCPLayoutElement::upPreparation);
10872 mPlotLayout->update(QCPLayoutElement::upMargins);
10873 mPlotLayout->update(QCPLayoutElement::upLayout);
10875 // draw viewport background pixmap:
10876 drawBackground(painter);
10878 // draw all layered objects (grid, axes, plottables, items, legend,...):
10879 foreach (QCPLayer *layer, mLayers)
10881 foreach (QCPLayerable *child, layer->children())
10883 if (child->realVisibility())
10885 painter->save();
10886 painter->setClipRect(child->clipRect().translated(0, -1));
10887 child->applyDefaultAntialiasingHint(painter);
10888 child->draw(painter);
10889 painter->restore();
10894 /* Debug code to draw all layout element rects
10895 foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10897 painter->setBrush(Qt::NoBrush);
10898 painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10899 painter->drawRect(el->rect());
10900 painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10901 painter->drawRect(el->outerRect());
10906 /*! \internal
10908 Draws the viewport background pixmap of the plot.
10910 If a pixmap was provided via \ref setBackground, this function buffers the scaled version
10911 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
10912 the viewport with the provided \a painter. The scaled version is buffered in
10913 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10914 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
10915 dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
10916 set.
10918 Note that this function does not draw a fill with the background brush (\ref setBackground(const
10919 QBrush &brush)) beneath the pixmap.
10921 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
10923 void QCustomPlot::drawBackground(QCPPainter *painter)
10925 // Note: background color is handled in individual replot/save functions
10927 // draw background pixmap (on top of fill, if brush specified):
10928 if (!mBackgroundPixmap.isNull())
10930 if (mBackgroundScaled)
10932 // check whether mScaledBackground needs to be updated:
10933 QSize scaledSize(mBackgroundPixmap.size());
10934 scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10935 if (mScaledBackgroundPixmap.size() != scaledSize)
10936 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10937 painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10938 } else
10940 painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10946 /*! \internal
10948 This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot
10949 so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly.
10951 void QCustomPlot::axisRemoved(QCPAxis *axis)
10953 if (xAxis == axis)
10954 xAxis = 0;
10955 if (xAxis2 == axis)
10956 xAxis2 = 0;
10957 if (yAxis == axis)
10958 yAxis = 0;
10959 if (yAxis2 == axis)
10960 yAxis2 = 0;
10962 // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
10965 /*! \internal
10967 This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so
10968 it may clear its QCustomPlot::legend member accordingly.
10970 void QCustomPlot::legendRemoved(QCPLegend *legend)
10972 if (this->legend == legend)
10973 this->legend = 0;
10976 /*! \internal
10978 Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called
10979 after every operation that changes the layer indices, like layer removal, layer creation, layer
10980 moving.
10982 void QCustomPlot::updateLayerIndices() const
10984 for (int i=0; i<mLayers.size(); ++i)
10985 mLayers.at(i)->mIndex = i;
10988 /*! \internal
10990 Returns the layerable at pixel position \a pos. If \a onlySelectable is set to true, only those
10991 layerables that are selectable will be considered. (Layerable subclasses communicate their
10992 selectability via the QCPLayerable::selectTest method, by returning -1.)
10994 \a selectionDetails is an output parameter that contains selection specifics of the affected
10995 layerable. This is useful if the respective layerable shall be given a subsequent
10996 QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains
10997 information about which part of the layerable was hit, in multi-part layerables (e.g.
10998 QCPAxis::SelectablePart).
11000 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11002 for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11004 const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11005 double minimumDistance = selectionTolerance()*1.1;
11006 QCPLayerable *minimumDistanceLayerable = 0;
11007 for (int i=layerables.size()-1; i>=0; --i)
11009 if (!layerables.at(i)->realVisibility())
11010 continue;
11011 QVariant details;
11012 double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11013 if (dist >= 0 && dist < minimumDistance)
11015 minimumDistance = dist;
11016 minimumDistanceLayerable = layerables.at(i);
11017 if (selectionDetails) *selectionDetails = details;
11020 if (minimumDistance < selectionTolerance())
11021 return minimumDistanceLayerable;
11023 return 0;
11027 Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is
11028 sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead
11029 to a full resolution file with width 200.) If the \a format supports compression, \a quality may
11030 be between 0 and 100 to control it.
11032 Returns true on success. If this function fails, most likely the given \a format isn't supported
11033 by the system, see Qt docs about QImageWriter::supportedImageFormats().
11035 \see saveBmp, saveJpg, savePng, savePdf
11037 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11039 QPixmap buffer = toPixmap(width, height, scale);
11040 if (!buffer.isNull())
11041 return buffer.save(fileName, format, quality);
11042 else
11043 return false;
11047 Renders the plot to a pixmap and returns it.
11049 The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and
11050 scale 2.0 lead to a full resolution pixmap with width 200.)
11052 \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf
11054 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11056 // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11057 int newWidth, newHeight;
11058 if (width == 0 || height == 0)
11060 newWidth = this->width();
11061 newHeight = this->height();
11062 } else
11064 newWidth = width;
11065 newHeight = height;
11067 int scaledWidth = qRound(scale*newWidth);
11068 int scaledHeight = qRound(scale*newHeight);
11070 QPixmap result(scaledWidth, scaledHeight);
11071 result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11072 QCPPainter painter;
11073 painter.begin(&result);
11074 if (painter.isActive())
11076 QRect oldViewport = viewport();
11077 setViewport(QRect(0, 0, newWidth, newHeight));
11078 painter.setMode(QCPPainter::pmNoCaching);
11079 if (!qFuzzyCompare(scale, 1.0))
11081 if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11082 painter.setMode(QCPPainter::pmNonCosmetic);
11083 painter.scale(scale, scale);
11085 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
11086 painter.fillRect(mViewport, mBackgroundBrush);
11087 draw(&painter);
11088 setViewport(oldViewport);
11089 painter.end();
11090 } else // might happen if pixmap has width or height zero
11092 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11093 return QPixmap();
11095 return result;
11099 Renders the plot using the passed \a painter.
11101 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
11102 appear scaled accordingly.
11104 \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter
11105 on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with
11106 the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter.
11108 \see toPixmap
11110 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11112 // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11113 int newWidth, newHeight;
11114 if (width == 0 || height == 0)
11116 newWidth = this->width();
11117 newHeight = this->height();
11118 } else
11120 newWidth = width;
11121 newHeight = height;
11124 if (painter->isActive())
11126 QRect oldViewport = viewport();
11127 setViewport(QRect(0, 0, newWidth, newHeight));
11128 painter->setMode(QCPPainter::pmNoCaching);
11129 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
11130 painter->fillRect(mViewport, mBackgroundBrush);
11131 draw(painter);
11132 setViewport(oldViewport);
11133 } else
11134 qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11138 ////////////////////////////////////////////////////////////////////////////////////////////////////
11139 //////////////////// QCPColorGradient
11140 ////////////////////////////////////////////////////////////////////////////////////////////////////
11142 /*! \class QCPColorGradient
11143 \brief Defines a color gradient for use with e.g. \ref QCPColorMap
11145 This class describes a color gradient which can be used to encode data with color. For example,
11146 QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which
11147 take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
11148 with a \a position from 0 to 1. In between these defined color positions, the
11149 color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
11151 Alternatively, load one of the preset color gradients shown in the image below, with \ref
11152 loadPreset, or by directly specifying the preset in the constructor.
11154 \image html QCPColorGradient.png
11156 The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
11157 converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
11158 GradientPreset to all the \a setGradient methods, e.g.:
11159 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient
11161 The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
11162 color gradient shall be applied periodically (wrapping around) to data values that lie outside
11163 the data range specified on the plottable instance can be controlled with \ref setPeriodic.
11167 Constructs a new QCPColorGradient initialized with the colors and color interpolation according
11168 to \a preset.
11170 The color level count is initialized to 350.
11172 QCPColorGradient::QCPColorGradient(GradientPreset preset) :
11173 mLevelCount(350),
11174 mColorInterpolation(ciRGB),
11175 mPeriodic(false),
11176 mColorBufferInvalidated(true)
11178 mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11179 loadPreset(preset);
11182 /* undocumented operator */
11183 bool QCPColorGradient::operator==(const QCPColorGradient &other) const
11185 return ((other.mLevelCount == this->mLevelCount) &&
11186 (other.mColorInterpolation == this->mColorInterpolation) &&
11187 (other.mPeriodic == this->mPeriodic) &&
11188 (other.mColorStops == this->mColorStops));
11192 Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
11193 is typically enough to create a smooth appearance.
11195 \image html QCPColorGradient-levelcount.png
11197 void QCPColorGradient::setLevelCount(int n)
11199 if (n < 2)
11201 qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11202 n = 2;
11204 if (n != mLevelCount)
11206 mLevelCount = n;
11207 mColorBufferInvalidated = true;
11212 Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
11213 colors are the values of the passed QMap \a colorStops. In between these color stops, the color
11214 is interpolated according to \ref setColorInterpolation.
11216 A more convenient way to create a custom gradient may be to clear all color stops with \ref
11217 clearColorStops and then adding them one by one with \ref setColorStopAt.
11219 \see clearColorStops
11221 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11223 mColorStops = colorStops;
11224 mColorBufferInvalidated = true;
11228 Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
11229 these color stops, the color is interpolated according to \ref setColorInterpolation.
11231 \see setColorStops, clearColorStops
11233 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11235 mColorStops.insert(position, color);
11236 mColorBufferInvalidated = true;
11240 Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
11241 interpolated linearly in RGB or in HSV color space.
11243 For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
11244 whereas in HSV space the intermediate color is yellow.
11246 void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
11248 if (interpolation != mColorInterpolation)
11250 mColorInterpolation = interpolation;
11251 mColorBufferInvalidated = true;
11256 Sets whether data points that are outside the configured data range (e.g. \ref
11257 QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
11258 they all have the same color, corresponding to the respective gradient boundary color.
11260 \image html QCPColorGradient-periodic.png
11262 As shown in the image above, gradients that have the same start and end color are especially
11263 suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
11264 the color map. A preset that has this property is \ref gpHues.
11266 In practice, using periodic color gradients makes sense when the data corresponds to a periodic
11267 dimension, such as an angle or a phase. If this is not the case, the color encoding might become
11268 ambiguous, because multiple different data values are shown as the same color.
11270 void QCPColorGradient::setPeriodic(bool enabled)
11272 mPeriodic = enabled;
11276 This method is used to quickly convert a \a data array to colors. The colors will be output in
11277 the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
11278 function. The data range that shall be used for mapping the data value to the gradient is passed
11279 in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
11280 logarithmically.
11282 if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
11283 set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
11284 array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
11285 is addressed <tt>data[i*dataIndexFactor]</tt>.
11287 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11289 // If you change something here, make sure to also adapt ::color()
11290 if (!data)
11292 qDebug() << Q_FUNC_INFO << "null pointer given as data";
11293 return;
11295 if (!scanLine)
11297 qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11298 return;
11300 if (mColorBufferInvalidated)
11301 updateColorBuffer();
11303 if (!logarithmic)
11305 const double posToIndexFactor = (mLevelCount-1)/range.size();
11306 if (mPeriodic)
11308 for (int i=0; i<n; ++i)
11310 int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11311 if (index < 0)
11312 index += mLevelCount;
11313 scanLine[i] = mColorBuffer.at(index);
11315 } else
11317 for (int i=0; i<n; ++i)
11319 int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11320 if (index < 0)
11321 index = 0;
11322 else if (index >= mLevelCount)
11323 index = mLevelCount-1;
11324 scanLine[i] = mColorBuffer.at(index);
11327 } else // logarithmic == true
11329 if (mPeriodic)
11331 for (int i=0; i<n; ++i)
11333 int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11334 if (index < 0)
11335 index += mLevelCount;
11336 scanLine[i] = mColorBuffer.at(index);
11338 } else
11340 for (int i=0; i<n; ++i)
11342 int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11343 if (index < 0)
11344 index = 0;
11345 else if (index >= mLevelCount)
11346 index = mLevelCount-1;
11347 scanLine[i] = mColorBuffer.at(index);
11353 /*! \internal
11355 This method is used to colorize a single data value given in \a position, to colors. The data
11356 range that shall be used for mapping the data value to the gradient is passed in \a range. \a
11357 logarithmic indicates whether the data value shall be mapped to a color logarithmically.
11359 If an entire array of data values shall be converted, rather use \ref colorize, for better
11360 performance.
11362 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11364 // If you change something here, make sure to also adapt ::colorize()
11365 if (mColorBufferInvalidated)
11366 updateColorBuffer();
11367 int index = 0;
11368 if (!logarithmic)
11369 index = (position-range.lower)*(mLevelCount-1)/range.size();
11370 else
11371 index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11372 if (mPeriodic)
11374 index = index % mLevelCount;
11375 if (index < 0)
11376 index += mLevelCount;
11377 } else
11379 if (index < 0)
11380 index = 0;
11381 else if (index >= mLevelCount)
11382 index = mLevelCount-1;
11384 return mColorBuffer.at(index);
11388 Clears the current color stops and loads the specified \a preset. A preset consists of predefined
11389 color stops and the corresponding color interpolation method.
11391 The available presets are:
11392 \image html QCPColorGradient.png
11394 void QCPColorGradient::loadPreset(GradientPreset preset)
11396 clearColorStops();
11397 switch (preset)
11399 case gpGrayscale:
11400 setColorInterpolation(ciRGB);
11401 setColorStopAt(0, Qt::black);
11402 setColorStopAt(1, Qt::white);
11403 break;
11404 case gpHot:
11405 setColorInterpolation(ciRGB);
11406 setColorStopAt(0, QColor(50, 0, 0));
11407 setColorStopAt(0.2, QColor(180, 10, 0));
11408 setColorStopAt(0.4, QColor(245, 50, 0));
11409 setColorStopAt(0.6, QColor(255, 150, 10));
11410 setColorStopAt(0.8, QColor(255, 255, 50));
11411 setColorStopAt(1, QColor(255, 255, 255));
11412 break;
11413 case gpCold:
11414 setColorInterpolation(ciRGB);
11415 setColorStopAt(0, QColor(0, 0, 50));
11416 setColorStopAt(0.2, QColor(0, 10, 180));
11417 setColorStopAt(0.4, QColor(0, 50, 245));
11418 setColorStopAt(0.6, QColor(10, 150, 255));
11419 setColorStopAt(0.8, QColor(50, 255, 255));
11420 setColorStopAt(1, QColor(255, 255, 255));
11421 break;
11422 case gpNight:
11423 setColorInterpolation(ciHSV);
11424 setColorStopAt(0, QColor(10, 20, 30));
11425 setColorStopAt(1, QColor(250, 255, 250));
11426 break;
11427 case gpCandy:
11428 setColorInterpolation(ciHSV);
11429 setColorStopAt(0, QColor(0, 0, 255));
11430 setColorStopAt(1, QColor(255, 250, 250));
11431 break;
11432 case gpGeography:
11433 setColorInterpolation(ciRGB);
11434 setColorStopAt(0, QColor(70, 170, 210));
11435 setColorStopAt(0.20, QColor(90, 160, 180));
11436 setColorStopAt(0.25, QColor(45, 130, 175));
11437 setColorStopAt(0.30, QColor(100, 140, 125));
11438 setColorStopAt(0.5, QColor(100, 140, 100));
11439 setColorStopAt(0.6, QColor(130, 145, 120));
11440 setColorStopAt(0.7, QColor(140, 130, 120));
11441 setColorStopAt(0.9, QColor(180, 190, 190));
11442 setColorStopAt(1, QColor(210, 210, 230));
11443 break;
11444 case gpIon:
11445 setColorInterpolation(ciHSV);
11446 setColorStopAt(0, QColor(50, 10, 10));
11447 setColorStopAt(0.45, QColor(0, 0, 255));
11448 setColorStopAt(0.8, QColor(0, 255, 255));
11449 setColorStopAt(1, QColor(0, 255, 0));
11450 break;
11451 case gpThermal:
11452 setColorInterpolation(ciRGB);
11453 setColorStopAt(0, QColor(0, 0, 50));
11454 setColorStopAt(0.15, QColor(20, 0, 120));
11455 setColorStopAt(0.33, QColor(200, 30, 140));
11456 setColorStopAt(0.6, QColor(255, 100, 0));
11457 setColorStopAt(0.85, QColor(255, 255, 40));
11458 setColorStopAt(1, QColor(255, 255, 255));
11459 break;
11460 case gpPolar:
11461 setColorInterpolation(ciRGB);
11462 setColorStopAt(0, QColor(50, 255, 255));
11463 setColorStopAt(0.18, QColor(10, 70, 255));
11464 setColorStopAt(0.28, QColor(10, 10, 190));
11465 setColorStopAt(0.5, QColor(0, 0, 0));
11466 setColorStopAt(0.72, QColor(190, 10, 10));
11467 setColorStopAt(0.82, QColor(255, 70, 10));
11468 setColorStopAt(1, QColor(255, 255, 50));
11469 break;
11470 case gpSpectrum:
11471 setColorInterpolation(ciHSV);
11472 setColorStopAt(0, QColor(50, 0, 50));
11473 setColorStopAt(0.15, QColor(0, 0, 255));
11474 setColorStopAt(0.35, QColor(0, 255, 255));
11475 setColorStopAt(0.6, QColor(255, 255, 0));
11476 setColorStopAt(0.75, QColor(255, 30, 0));
11477 setColorStopAt(1, QColor(50, 0, 0));
11478 break;
11479 case gpJet:
11480 setColorInterpolation(ciRGB);
11481 setColorStopAt(0, QColor(0, 0, 100));
11482 setColorStopAt(0.15, QColor(0, 50, 255));
11483 setColorStopAt(0.35, QColor(0, 255, 255));
11484 setColorStopAt(0.65, QColor(255, 255, 0));
11485 setColorStopAt(0.85, QColor(255, 30, 0));
11486 setColorStopAt(1, QColor(100, 0, 0));
11487 break;
11488 case gpHues:
11489 setColorInterpolation(ciHSV);
11490 setColorStopAt(0, QColor(255, 0, 0));
11491 setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11492 setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11493 setColorStopAt(1, QColor(255, 0, 0));
11494 break;
11499 Clears all color stops.
11501 \see setColorStops, setColorStopAt
11503 void QCPColorGradient::clearColorStops()
11505 mColorStops.clear();
11506 mColorBufferInvalidated = true;
11510 Returns an inverted gradient. The inverted gradient has all properties as this \ref
11511 QCPColorGradient, but the order of the color stops is inverted.
11513 \see setColorStops, setColorStopAt
11515 QCPColorGradient QCPColorGradient::inverted() const
11517 QCPColorGradient result(*this);
11518 result.clearColorStops();
11519 for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11520 result.setColorStopAt(1.0-it.key(), it.value());
11521 return result;
11524 /*! \internal
11526 Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
11527 convert positions to colors. This is where the interpolation between color stops is calculated.
11529 void QCPColorGradient::updateColorBuffer()
11531 if (mColorBuffer.size() != mLevelCount)
11532 mColorBuffer.resize(mLevelCount);
11533 if (mColorStops.size() > 1)
11535 double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11536 for (int i=0; i<mLevelCount; ++i)
11538 double position = i*indexToPosFactor;
11539 QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11540 if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11542 mColorBuffer[i] = (it-1).value().rgb();
11543 } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11545 mColorBuffer[i] = it.value().rgb();
11546 } else // position is in between stops (or on an intermediate stop), interpolate color
11548 QMap<double, QColor>::const_iterator high = it;
11549 QMap<double, QColor>::const_iterator low = it-1;
11550 double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11551 switch (mColorInterpolation)
11553 case ciRGB:
11555 mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11556 (1-t)*low.value().green() + t*high.value().green(),
11557 (1-t)*low.value().blue() + t*high.value().blue());
11558 break;
11560 case ciHSV:
11562 QColor lowHsv = low.value().toHsv();
11563 QColor highHsv = high.value().toHsv();
11564 double hue = 0;
11565 double hueDiff = highHsv.hueF()-lowHsv.hueF();
11566 if (hueDiff > 0.5)
11567 hue = lowHsv.hueF() - t*(1.0-hueDiff);
11568 else if (hueDiff < -0.5)
11569 hue = lowHsv.hueF() + t*(1.0+hueDiff);
11570 else
11571 hue = lowHsv.hueF() + t*hueDiff;
11572 if (hue < 0) hue += 1.0;
11573 else if (hue >= 1.0) hue -= 1.0;
11574 mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11575 break;
11580 } else if (mColorStops.size() == 1)
11582 mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11583 } else // mColorStops is empty, fill color buffer with black
11585 mColorBuffer.fill(qRgb(0, 0, 0));
11587 mColorBufferInvalidated = false;
11591 ////////////////////////////////////////////////////////////////////////////////////////////////////
11592 //////////////////// QCPAxisRect
11593 ////////////////////////////////////////////////////////////////////////////////////////////////////
11595 /*! \class QCPAxisRect
11596 \brief Holds multiple axes and arranges them in a rectangular shape.
11598 This class represents an axis rect, a rectangular area that is bounded on all sides with an
11599 arbitrary number of axes.
11601 Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
11602 layout system allows to have multiple axis rects, e.g. arranged in a grid layout
11603 (QCustomPlot::plotLayout).
11605 By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
11606 accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
11607 If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
11608 invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
11609 addAxes. To remove an axis, use \ref removeAxis.
11611 The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
11612 setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
11613 explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
11614 placed on other layers, independently of the axis rect.
11616 Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
11617 insetLayout and can be used to have other layout elements (or even other layouts with multiple
11618 elements) hovering inside the axis rect.
11620 If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
11621 behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
11622 is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
11623 via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
11624 only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
11625 QCP::iRangeZoom.
11627 \image html AxisRectSpacingOverview.png
11628 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
11629 line on the far left indicates the viewport/widget border.</center>
11632 /* start documentation of inline functions */
11634 /*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
11636 Returns the inset layout of this axis rect. It can be used to place other layout elements (or
11637 even layouts with multiple other elements) inside/on top of an axis rect.
11639 \see QCPLayoutInset
11642 /*! \fn int QCPAxisRect::left() const
11644 Returns the pixel position of the left border of this axis rect. Margins are not taken into
11645 account here, so the returned value is with respect to the inner \ref rect.
11648 /*! \fn int QCPAxisRect::right() const
11650 Returns the pixel position of the right 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::top() const
11656 Returns the pixel position of the top 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::bottom() const
11662 Returns the pixel position of the bottom 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::width() const
11668 Returns the pixel width of this axis rect. Margins are not taken into account here, so the
11669 returned value is with respect to the inner \ref rect.
11672 /*! \fn int QCPAxisRect::height() const
11674 Returns the pixel height 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 QSize QCPAxisRect::size() const
11680 Returns the pixel size 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 QPoint QCPAxisRect::topLeft() const
11686 Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
11687 so the returned value is with respect to the inner \ref rect.
11690 /*! \fn QPoint QCPAxisRect::topRight() const
11692 Returns the top right corner of this axis rect in pixels. Margins are not taken into account
11693 here, so the returned value is with respect to the inner \ref rect.
11696 /*! \fn QPoint QCPAxisRect::bottomLeft() const
11698 Returns the bottom left 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::bottomRight() const
11704 Returns the bottom right 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::center() const
11710 Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
11711 returned value is with respect to the inner \ref rect.
11714 /* end documentation of inline functions */
11717 Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
11718 sides, the top and right axes are set invisible initially.
11720 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11721 QCPLayoutElement(parentPlot),
11722 mBackgroundBrush(Qt::NoBrush),
11723 mBackgroundScaled(true),
11724 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11725 mInsetLayout(new QCPLayoutInset),
11726 mRangeDrag(Qt::Horizontal|Qt::Vertical),
11727 mRangeZoom(Qt::Horizontal|Qt::Vertical),
11728 mRangeZoomFactorHorz(0.85),
11729 mRangeZoomFactorVert(0.85),
11730 mDragging(false)
11732 mInsetLayout->initializeParentPlot(mParentPlot);
11733 mInsetLayout->setParentLayerable(this);
11734 mInsetLayout->setParent(this);
11736 setMinimumSize(50, 50);
11737 setMinimumMargins(QMargins(15, 15, 15, 15));
11738 mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11739 mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11740 mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11741 mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11743 if (setupDefaultAxes)
11745 QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11746 QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11747 QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11748 QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11749 setRangeDragAxes(xAxis, yAxis);
11750 setRangeZoomAxes(xAxis, yAxis);
11751 xAxis2->setVisible(false);
11752 yAxis2->setVisible(false);
11753 xAxis->grid()->setVisible(true);
11754 yAxis->grid()->setVisible(true);
11755 xAxis2->grid()->setVisible(false);
11756 yAxis2->grid()->setVisible(false);
11757 xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11758 yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11759 xAxis2->grid()->setVisible(false);
11760 yAxis2->grid()->setVisible(false);
11764 QCPAxisRect::~QCPAxisRect()
11766 delete mInsetLayout;
11767 mInsetLayout = 0;
11769 QList<QCPAxis*> axesList = axes();
11770 for (int i=0; i<axesList.size(); ++i)
11771 removeAxis(axesList.at(i));
11775 Returns the number of axes on the axis rect side specified with \a type.
11777 \see axis
11779 int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
11781 return mAxes.value(type).size();
11785 Returns the axis with the given \a index on the axis rect side specified with \a type.
11787 \see axisCount, axes
11789 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
11791 QList<QCPAxis*> ax(mAxes.value(type));
11792 if (index >= 0 && index < ax.size())
11794 return ax.at(index);
11795 } else
11797 qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11798 return 0;
11803 Returns all axes on the axis rect sides specified with \a types.
11805 \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
11806 multiple sides.
11808 \see axis
11810 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11812 QList<QCPAxis*> result;
11813 if (types.testFlag(QCPAxis::atLeft))
11814 result << mAxes.value(QCPAxis::atLeft);
11815 if (types.testFlag(QCPAxis::atRight))
11816 result << mAxes.value(QCPAxis::atRight);
11817 if (types.testFlag(QCPAxis::atTop))
11818 result << mAxes.value(QCPAxis::atTop);
11819 if (types.testFlag(QCPAxis::atBottom))
11820 result << mAxes.value(QCPAxis::atBottom);
11821 return result;
11824 /*! \overload
11826 Returns all axes of this axis rect.
11828 QList<QCPAxis*> QCPAxisRect::axes() const
11830 QList<QCPAxis*> result;
11831 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11832 while (it.hasNext())
11834 it.next();
11835 result << it.value();
11837 return result;
11841 Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a
11842 new QCPAxis instance is created internally.
11844 You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis to an axis that was
11845 previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
11846 of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
11847 with this axis rect as parent and with the same axis type as specified in \a type. If this is not
11848 the case, a debug output is generated, the axis is not added, and the method returns 0.
11850 This method can not be used to move \a axis between axis rects. The same \a axis instance must
11851 not be added multiple times to the same or different axis rects.
11853 If an axis rect side already contains one or more axes, the lower and upper endings of the new
11854 axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref
11855 QCPLineEnding::esHalfBar.
11857 \see addAxes, setupFullAxesBox
11859 QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
11861 QCPAxis *newAxis = axis;
11862 if (!newAxis)
11864 newAxis = new QCPAxis(this, type);
11865 } else // user provided existing axis instance, do some sanity checks
11867 if (newAxis->axisType() != type)
11869 qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
11870 return 0;
11872 if (newAxis->axisRect() != this)
11874 qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
11875 return 0;
11877 if (axes().contains(newAxis))
11879 qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
11880 return 0;
11883 if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11885 bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11886 newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11887 newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11889 mAxes[type].append(newAxis);
11890 return newAxis;
11894 Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
11895 <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
11897 Returns a list of the added axes.
11899 \see addAxis, setupFullAxesBox
11901 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11903 QList<QCPAxis*> result;
11904 if (types.testFlag(QCPAxis::atLeft))
11905 result << addAxis(QCPAxis::atLeft);
11906 if (types.testFlag(QCPAxis::atRight))
11907 result << addAxis(QCPAxis::atRight);
11908 if (types.testFlag(QCPAxis::atTop))
11909 result << addAxis(QCPAxis::atTop);
11910 if (types.testFlag(QCPAxis::atBottom))
11911 result << addAxis(QCPAxis::atBottom);
11912 return result;
11916 Removes the specified \a axis from the axis rect and deletes it.
11918 Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
11920 \see addAxis
11922 bool QCPAxisRect::removeAxis(QCPAxis *axis)
11924 // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11925 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11926 while (it.hasNext())
11928 it.next();
11929 if (it.value().contains(axis))
11931 mAxes[it.key()].removeOne(axis);
11932 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)
11933 parentPlot()->axisRemoved(axis);
11934 delete axis;
11935 return true;
11938 qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11939 return false;
11943 Convenience function to create an axis on each side that doesn't have any axes yet and set their
11944 visibility to true. Further, the top/right axes are assigned the following properties of the
11945 bottom/left axes:
11947 \li range (\ref QCPAxis::setRange)
11948 \li range reversed (\ref QCPAxis::setRangeReversed)
11949 \li scale type (\ref QCPAxis::setScaleType)
11950 \li scale log base (\ref QCPAxis::setScaleLogBase)
11951 \li ticks (\ref QCPAxis::setTicks)
11952 \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
11953 \li sub tick count (\ref QCPAxis::setSubTickCount)
11954 \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
11955 \li tick step (\ref QCPAxis::setTickStep)
11956 \li auto tick step (\ref QCPAxis::setAutoTickStep)
11957 \li number format (\ref QCPAxis::setNumberFormat)
11958 \li number precision (\ref QCPAxis::setNumberPrecision)
11959 \li tick label type (\ref QCPAxis::setTickLabelType)
11960 \li date time format (\ref QCPAxis::setDateTimeFormat)
11961 \li date time spec (\ref QCPAxis::setDateTimeSpec)
11963 Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
11965 If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
11966 and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
11968 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11970 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11971 if (axisCount(QCPAxis::atBottom) == 0)
11972 xAxis = addAxis(QCPAxis::atBottom);
11973 else
11974 xAxis = axis(QCPAxis::atBottom);
11976 if (axisCount(QCPAxis::atLeft) == 0)
11977 yAxis = addAxis(QCPAxis::atLeft);
11978 else
11979 yAxis = axis(QCPAxis::atLeft);
11981 if (axisCount(QCPAxis::atTop) == 0)
11982 xAxis2 = addAxis(QCPAxis::atTop);
11983 else
11984 xAxis2 = axis(QCPAxis::atTop);
11986 if (axisCount(QCPAxis::atRight) == 0)
11987 yAxis2 = addAxis(QCPAxis::atRight);
11988 else
11989 yAxis2 = axis(QCPAxis::atRight);
11991 xAxis->setVisible(true);
11992 yAxis->setVisible(true);
11993 xAxis2->setVisible(true);
11994 yAxis2->setVisible(true);
11995 xAxis2->setTickLabels(false);
11996 yAxis2->setTickLabels(false);
11998 xAxis2->setRange(xAxis->range());
11999 xAxis2->setRangeReversed(xAxis->rangeReversed());
12000 xAxis2->setScaleType(xAxis->scaleType());
12001 xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12002 xAxis2->setTicks(xAxis->ticks());
12003 xAxis2->setAutoTickCount(xAxis->autoTickCount());
12004 xAxis2->setSubTickCount(xAxis->subTickCount());
12005 xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12006 xAxis2->setTickStep(xAxis->tickStep());
12007 xAxis2->setAutoTickStep(xAxis->autoTickStep());
12008 xAxis2->setNumberFormat(xAxis->numberFormat());
12009 xAxis2->setNumberPrecision(xAxis->numberPrecision());
12010 xAxis2->setTickLabelType(xAxis->tickLabelType());
12011 xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12012 xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12014 yAxis2->setRange(yAxis->range());
12015 yAxis2->setRangeReversed(yAxis->rangeReversed());
12016 yAxis2->setScaleType(yAxis->scaleType());
12017 yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12018 yAxis2->setTicks(yAxis->ticks());
12019 yAxis2->setAutoTickCount(yAxis->autoTickCount());
12020 yAxis2->setSubTickCount(yAxis->subTickCount());
12021 yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12022 yAxis2->setTickStep(yAxis->tickStep());
12023 yAxis2->setAutoTickStep(yAxis->autoTickStep());
12024 yAxis2->setNumberFormat(yAxis->numberFormat());
12025 yAxis2->setNumberPrecision(yAxis->numberPrecision());
12026 yAxis2->setTickLabelType(yAxis->tickLabelType());
12027 yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12028 yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12030 if (connectRanges)
12032 connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12033 connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12038 Returns a list of all the plottables that are associated with this axis rect.
12040 A plottable is considered associated with an axis rect if its key or value axis (or both) is in
12041 this axis rect.
12043 \see graphs, items
12045 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12047 // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12048 QList<QCPAbstractPlottable*> result;
12049 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12051 if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12052 result.append(mParentPlot->mPlottables.at(i));
12054 return result;
12058 Returns a list of all the graphs that are associated with this axis rect.
12060 A graph is considered associated with an axis rect if its key or value axis (or both) is in
12061 this axis rect.
12063 \see plottables, items
12065 QList<QCPGraph*> QCPAxisRect::graphs() const
12067 // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12068 QList<QCPGraph*> result;
12069 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12071 if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12072 result.append(mParentPlot->mGraphs.at(i));
12074 return result;
12078 Returns a list of all the items that are associated with this axis rect.
12080 An item is considered associated with an axis rect if any of its positions has key or value axis
12081 set to an axis that is in this axis rect, or if any of its positions has \ref
12082 QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
12083 QCPAbstractItem::setClipAxisRect) is set to this axis rect.
12085 \see plottables, graphs
12087 QList<QCPAbstractItem *> QCPAxisRect::items() const
12089 // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12090 // and miss those items that have this axis rect as clipAxisRect.
12091 QList<QCPAbstractItem*> result;
12092 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12094 if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12096 result.append(mParentPlot->mItems.at(itemId));
12097 continue;
12099 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12100 for (int posId=0; posId<positions.size(); ++posId)
12102 if (positions.at(posId)->axisRect() == this ||
12103 positions.at(posId)->keyAxis()->axisRect() == this ||
12104 positions.at(posId)->valueAxis()->axisRect() == this)
12106 result.append(mParentPlot->mItems.at(itemId));
12107 break;
12111 return result;
12115 This method is called automatically upon replot and doesn't need to be called by users of
12116 QCPAxisRect.
12118 Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
12119 and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
12120 QCPInsetLayout::update function.
12122 void QCPAxisRect::update(UpdatePhase phase)
12124 QCPLayoutElement::update(phase);
12126 switch (phase)
12128 case upPreparation:
12130 QList<QCPAxis*> allAxes = axes();
12131 for (int i=0; i<allAxes.size(); ++i)
12132 allAxes.at(i)->setupTickVectors();
12133 break;
12135 case upLayout:
12137 mInsetLayout->setOuterRect(rect());
12138 break;
12140 default: break;
12143 // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12144 mInsetLayout->update(phase);
12147 /* inherits documentation from base class */
12148 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12150 QList<QCPLayoutElement*> result;
12151 if (mInsetLayout)
12153 result << mInsetLayout;
12154 if (recursive)
12155 result << mInsetLayout->elements(recursive);
12157 return result;
12160 /* inherits documentation from base class */
12161 void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
12163 painter->setAntialiasing(false);
12166 /* inherits documentation from base class */
12167 void QCPAxisRect::draw(QCPPainter *painter)
12169 drawBackground(painter);
12173 Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
12174 axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
12175 backgrounds are usually drawn below everything else.
12177 For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
12178 enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
12179 is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
12180 consider using the overloaded version of this function.
12182 Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
12183 setBackground(const QBrush &brush).
12185 \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
12187 void QCPAxisRect::setBackground(const QPixmap &pm)
12189 mBackgroundPixmap = pm;
12190 mScaledBackgroundPixmap = QPixmap();
12193 /*! \overload
12195 Sets \a brush as the background brush. The axis rect background will be filled with this brush.
12196 Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
12197 are usually drawn below everything else.
12199 The brush will be drawn before (under) any background pixmap, which may be specified with \ref
12200 setBackground(const QPixmap &pm).
12202 To disable drawing of a background brush, set \a brush to Qt::NoBrush.
12204 \see setBackground(const QPixmap &pm)
12206 void QCPAxisRect::setBackground(const QBrush &brush)
12208 mBackgroundBrush = brush;
12211 /*! \overload
12213 Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
12214 shall be scaled in one call.
12216 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
12218 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12220 mBackgroundPixmap = pm;
12221 mScaledBackgroundPixmap = QPixmap();
12222 mBackgroundScaled = scaled;
12223 mBackgroundScaledMode = mode;
12227 Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
12228 is set to true, you may control whether and how the aspect ratio of the original pixmap is
12229 preserved with \ref setBackgroundScaledMode.
12231 Note that the scaled version of the original pixmap is buffered, so there is no performance
12232 penalty on replots. (Except when the axis rect dimensions are changed continuously.)
12234 \see setBackground, setBackgroundScaledMode
12236 void QCPAxisRect::setBackgroundScaled(bool scaled)
12238 mBackgroundScaled = scaled;
12242 If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
12243 define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
12244 \see setBackground, setBackgroundScaled
12246 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12248 mBackgroundScaledMode = mode;
12252 Returns the range drag axis of the \a orientation provided.
12254 \see setRangeDragAxes
12256 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12258 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12262 Returns the range zoom axis of the \a orientation provided.
12264 \see setRangeZoomAxes
12266 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12268 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12272 Returns the range zoom factor of the \a orientation provided.
12274 \see setRangeZoomFactor
12276 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12278 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12282 Sets which axis orientation may be range dragged by the user with mouse interaction.
12283 What orientation corresponds to which specific axis can be set with
12284 \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
12285 default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
12286 is the left axis (yAxis).
12288 To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
12289 QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
12290 Qt::Vertical</tt> as \a orientations.
12292 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12293 contains \ref QCP::iRangeDrag to enable the range dragging interaction.
12295 \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag
12297 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12299 mRangeDrag = orientations;
12303 Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
12304 corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
12305 QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
12306 axis is the left axis (yAxis).
12308 To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
12309 QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
12310 Qt::Vertical</tt> as \a orientations.
12312 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12313 contains \ref QCP::iRangeZoom to enable the range zooming interaction.
12315 \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
12317 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12319 mRangeZoom = orientations;
12323 Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
12324 on the QCustomPlot widget.
12326 \see setRangeZoomAxes
12328 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12330 mRangeDragHorzAxis = horizontal;
12331 mRangeDragVertAxis = vertical;
12335 Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
12336 QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
12337 are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
12339 \see setRangeDragAxes
12341 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12343 mRangeZoomHorzAxis = horizontal;
12344 mRangeZoomVertAxis = vertical;
12348 Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
12349 \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
12350 let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
12351 and which is vertical, can be set with \ref setRangeZoomAxes.
12353 When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
12354 will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
12355 same scrolling direction will zoom out.
12357 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12359 mRangeZoomFactorHorz = horizontalFactor;
12360 mRangeZoomFactorVert = verticalFactor;
12363 /*! \overload
12365 Sets both the horizontal and vertical zoom \a factor.
12367 void QCPAxisRect::setRangeZoomFactor(double factor)
12369 mRangeZoomFactorHorz = factor;
12370 mRangeZoomFactorVert = factor;
12373 /*! \internal
12375 Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
12376 pixmap.
12378 If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
12379 according filling inside the axis rect with the provided \a painter.
12381 Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
12382 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
12383 the axis rect with the provided \a painter. The scaled version is buffered in
12384 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
12385 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
12386 dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
12387 set.
12389 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
12391 void QCPAxisRect::drawBackground(QCPPainter *painter)
12393 // draw background fill:
12394 if (mBackgroundBrush != Qt::NoBrush)
12395 painter->fillRect(mRect, mBackgroundBrush);
12397 // draw background pixmap (on top of fill, if brush specified):
12398 if (!mBackgroundPixmap.isNull())
12400 if (mBackgroundScaled)
12402 // check whether mScaledBackground needs to be updated:
12403 QSize scaledSize(mBackgroundPixmap.size());
12404 scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12405 if (mScaledBackgroundPixmap.size() != scaledSize)
12406 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12407 painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12408 } else
12410 painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12415 /*! \internal
12417 This function makes sure multiple axes on the side specified with \a type don't collide, but are
12418 distributed according to their respective space requirement (QCPAxis::calculateMargin).
12420 It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
12421 one with index zero.
12423 This function is called by \ref calculateAutoMargin.
12425 void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
12427 const QList<QCPAxis*> axesList = mAxes.value(type);
12428 if (axesList.isEmpty())
12429 return;
12431 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
12432 for (int i=1; i<axesList.size(); ++i)
12434 int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12435 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)
12437 if (!isFirstVisible)
12438 offset += axesList.at(i)->tickLengthIn();
12439 isFirstVisible = false;
12441 axesList.at(i)->setOffset(offset);
12445 /* inherits documentation from base class */
12446 int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
12448 if (!mAutoMargins.testFlag(side))
12449 qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12451 updateAxesOffset(QCPAxis::marginSideToAxisType(side));
12453 // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12454 const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12455 if (axesList.size() > 0)
12456 return axesList.last()->offset() + axesList.last()->calculateMargin();
12457 else
12458 return 0;
12461 /*! \internal
12463 Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
12464 pressed, the range dragging interaction is initialized (the actual range manipulation happens in
12465 the \ref mouseMoveEvent).
12467 The mDragging flag is set to true and some anchor points are set that are needed to determine the
12468 distance the mouse was dragged in the mouse move/release events later.
12470 \see mouseMoveEvent, mouseReleaseEvent
12472 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12474 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)
12475 if (event->buttons() & Qt::LeftButton)
12477 mDragging = true;
12478 // initialize antialiasing backup in case we start dragging:
12479 if (mParentPlot->noAntialiasingOnDrag())
12481 mAADragBackup = mParentPlot->antialiasedElements();
12482 mNotAADragBackup = mParentPlot->notAntialiasedElements();
12484 // Mouse range dragging interaction:
12485 if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12487 if (mRangeDragHorzAxis)
12488 mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12489 if (mRangeDragVertAxis)
12490 mDragStartVertRange = mRangeDragVertAxis.data()->range();
12495 /*! \internal
12497 Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
12498 preceding \ref mousePressEvent, the range is moved accordingly.
12500 \see mousePressEvent, mouseReleaseEvent
12502 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12504 // Mouse range dragging interaction:
12505 if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12507 if (mRangeDrag.testFlag(Qt::Horizontal))
12509 if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12511 if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12513 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12514 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12515 } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12517 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12518 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12522 if (mRangeDrag.testFlag(Qt::Vertical))
12524 if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12526 if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12528 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12529 rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12530 } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12532 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12533 rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12537 if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12539 if (mParentPlot->noAntialiasingOnDrag())
12540 mParentPlot->setNotAntialiasedElements(QCP::aeAll);
12541 mParentPlot->replot();
12546 /* inherits documentation from base class */
12547 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12549 Q_UNUSED(event)
12550 mDragging = false;
12551 if (mParentPlot->noAntialiasingOnDrag())
12553 mParentPlot->setAntialiasedElements(mAADragBackup);
12554 mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
12558 /*! \internal
12560 Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
12561 ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
12562 the scaling operation is the current cursor position inside the axis rect. The scaling factor is
12563 dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
12564 zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
12566 Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
12567 wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
12568 multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
12569 exponent of the range zoom factor. This takes care of the wheel direction automatically, by
12570 inverting the factor, when the wheel step is negative (f^-1 = 1/f).
12572 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12574 // Mouse range zooming interaction:
12575 if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12577 if (mRangeZoom != 0)
12579 double factor;
12580 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12581 if (mRangeZoom.testFlag(Qt::Horizontal))
12583 factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12584 if (mRangeZoomHorzAxis.data())
12585 mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12587 if (mRangeZoom.testFlag(Qt::Vertical))
12589 factor = qPow(mRangeZoomFactorVert, wheelSteps);
12590 if (mRangeZoomVertAxis.data())
12591 mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12593 mParentPlot->replot();
12599 ////////////////////////////////////////////////////////////////////////////////////////////////////
12600 //////////////////// QCPAbstractLegendItem
12601 ////////////////////////////////////////////////////////////////////////////////////////////////////
12603 /*! \class QCPAbstractLegendItem
12604 \brief The abstract base class for all entries in a QCPLegend.
12606 It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
12607 legend, the subclass \ref QCPPlottableLegendItem is more suitable.
12609 Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
12610 that's not even associated with a plottable).
12612 You must implement the following pure virtual functions:
12613 \li \ref draw (from QCPLayerable)
12615 You inherit the following members you may use:
12616 <table>
12617 <tr>
12618 <td>QCPLegend *\b mParentLegend</td>
12619 <td>A pointer to the parent QCPLegend.</td>
12620 </tr><tr>
12621 <td>QFont \b mFont</td>
12622 <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>
12623 </tr>
12624 </table>
12627 /* start of documentation of signals */
12629 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
12631 This signal is emitted when the selection state of this legend item has changed, either by user
12632 interaction or by a direct call to \ref setSelected.
12635 /* end of documentation of signals */
12638 Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
12639 cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
12641 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
12642 QCPLayoutElement(parent->parentPlot()),
12643 mParentLegend(parent),
12644 mFont(parent->font()),
12645 mTextColor(parent->textColor()),
12646 mSelectedFont(parent->selectedFont()),
12647 mSelectedTextColor(parent->selectedTextColor()),
12648 mSelectable(true),
12649 mSelected(false)
12651 setLayer(QLatin1String("legend"));
12652 setMargins(QMargins(8, 2, 8, 2));
12656 Sets the default font of this specific legend item to \a font.
12658 \see setTextColor, QCPLegend::setFont
12660 void QCPAbstractLegendItem::setFont(const QFont &font)
12662 mFont = font;
12666 Sets the default text color of this specific legend item to \a color.
12668 \see setFont, QCPLegend::setTextColor
12670 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12672 mTextColor = color;
12676 When this legend item is selected, \a font is used to draw generic text, instead of the normal
12677 font set with \ref setFont.
12679 \see setFont, QCPLegend::setSelectedFont
12681 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
12683 mSelectedFont = font;
12687 When this legend item is selected, \a color is used to draw generic text, instead of the normal
12688 color set with \ref setTextColor.
12690 \see setTextColor, QCPLegend::setSelectedTextColor
12692 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
12694 mSelectedTextColor = color;
12698 Sets whether this specific legend item is selectable.
12700 \see setSelectedParts, QCustomPlot::setInteractions
12702 void QCPAbstractLegendItem::setSelectable(bool selectable)
12704 if (mSelectable != selectable)
12706 mSelectable = selectable;
12707 emit selectableChanged(mSelectable);
12712 Sets whether this specific legend item is selected.
12714 It is possible to set the selection state of this item by calling this function directly, even if
12715 setSelectable is set to false.
12717 \see setSelectableParts, QCustomPlot::setInteractions
12719 void QCPAbstractLegendItem::setSelected(bool selected)
12721 if (mSelected != selected)
12723 mSelected = selected;
12724 emit selectionChanged(mSelected);
12728 /* inherits documentation from base class */
12729 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12731 Q_UNUSED(details)
12732 if (!mParentPlot) return -1;
12733 if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12734 return -1;
12736 if (mRect.contains(pos.toPoint()))
12737 return mParentPlot->selectionTolerance()*0.99;
12738 else
12739 return -1;
12742 /* inherits documentation from base class */
12743 void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
12745 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
12748 /* inherits documentation from base class */
12749 QRect QCPAbstractLegendItem::clipRect() const
12751 return mOuterRect;
12754 /* inherits documentation from base class */
12755 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12757 Q_UNUSED(event)
12758 Q_UNUSED(details)
12759 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12761 bool selBefore = mSelected;
12762 setSelected(additive ? !mSelected : true);
12763 if (selectionStateChanged)
12764 *selectionStateChanged = mSelected != selBefore;
12768 /* inherits documentation from base class */
12769 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12771 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12773 bool selBefore = mSelected;
12774 setSelected(false);
12775 if (selectionStateChanged)
12776 *selectionStateChanged = mSelected != selBefore;
12780 ////////////////////////////////////////////////////////////////////////////////////////////////////
12781 //////////////////// QCPPlottableLegendItem
12782 ////////////////////////////////////////////////////////////////////////////////////////////////////
12784 /*! \class QCPPlottableLegendItem
12785 \brief A legend item representing a plottable with an icon and the plottable name.
12787 This is the standard legend item for plottables. It displays an icon of the plottable next to the
12788 plottable name. The icon is drawn by the respective plottable itself (\ref
12789 QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
12790 For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
12791 middle.
12793 Legend items of this type are always associated with one plottable (retrievable via the
12794 plottable() function and settable with the constructor). You may change the font of the plottable
12795 name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
12796 QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
12798 The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
12799 creates/removes legend items of this type in the default implementation. However, these functions
12800 may be reimplemented such that a different kind of legend item (e.g a direct subclass of
12801 QCPAbstractLegendItem) is used for that plottable.
12803 Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
12804 QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
12805 interface, QCPLegend has specialized functions for handling legend items conveniently, see the
12806 documentation of \ref QCPLegend.
12810 Creates a new legend item associated with \a plottable.
12812 Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
12814 A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
12815 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
12817 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
12818 QCPAbstractLegendItem(parent),
12819 mPlottable(plottable)
12823 /*! \internal
12825 Returns the pen that shall be used to draw the icon border, taking into account the selection
12826 state of this item.
12828 QPen QCPPlottableLegendItem::getIconBorderPen() const
12830 return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
12833 /*! \internal
12835 Returns the text color that shall be used to draw text, taking into account the selection state
12836 of this item.
12838 QColor QCPPlottableLegendItem::getTextColor() const
12840 return mSelected ? mSelectedTextColor : mTextColor;
12843 /*! \internal
12845 Returns the font that shall be used to draw text, taking into account the selection state of this
12846 item.
12848 QFont QCPPlottableLegendItem::getFont() const
12850 return mSelected ? mSelectedFont : mFont;
12853 /*! \internal
12855 Draws the item with \a painter. The size and position of the drawn legend item is defined by the
12856 parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
12857 of this legend item.
12859 void QCPPlottableLegendItem::draw(QCPPainter *painter)
12861 if (!mPlottable) return;
12862 painter->setFont(getFont());
12863 painter->setPen(QPen(getTextColor()));
12864 QSizeF iconSize = mParentLegend->iconSize();
12865 QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12866 QRectF iconRect(mRect.topLeft(), iconSize);
12867 int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12868 painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12869 // draw icon:
12870 painter->save();
12871 painter->setClipRect(iconRect, Qt::IntersectClip);
12872 mPlottable->drawLegendIcon(painter, iconRect);
12873 painter->restore();
12874 // draw icon border:
12875 if (getIconBorderPen().style() != Qt::NoPen)
12877 painter->setPen(getIconBorderPen());
12878 painter->setBrush(Qt::NoBrush);
12879 painter->drawRect(iconRect);
12883 /*! \internal
12885 Calculates and returns the size of this item. This includes the icon, the text and the padding in
12886 between.
12888 QSize QCPPlottableLegendItem::minimumSizeHint() const
12890 if (!mPlottable) return QSize();
12891 QSize result(0, 0);
12892 QRect textRect;
12893 QFontMetrics fontMetrics(getFont());
12894 QSize iconSize = mParentLegend->iconSize();
12895 textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12896 result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12897 result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12898 return result;
12902 ////////////////////////////////////////////////////////////////////////////////////////////////////
12903 //////////////////// QCPLegend
12904 ////////////////////////////////////////////////////////////////////////////////////////////////////
12906 /*! \class QCPLegend
12907 \brief Manages a legend inside a QCustomPlot.
12909 A legend is a small box somewhere in the plot which lists plottables with their name and icon.
12911 Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
12912 respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
12913 QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
12914 itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
12916 The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
12917 QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
12918 placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
12919 handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
12920 other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
12921 However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
12922 only return the number of items with QCPAbstractLegendItems type).
12924 By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
12925 layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
12926 position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
12927 outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
12930 /* start of documentation of signals */
12932 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
12934 This signal is emitted when the selection state of this legend has changed.
12936 \see setSelectedParts, setSelectableParts
12939 /* end of documentation of signals */
12942 Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
12944 Note that by default, QCustomPlot already contains a legend ready to be used as
12945 QCustomPlot::legend
12947 QCPLegend::QCPLegend()
12949 setRowSpacing(0);
12950 setColumnSpacing(10);
12951 setMargins(QMargins(2, 3, 2, 2));
12952 setAntialiased(false);
12953 setIconSize(32, 18);
12955 setIconTextPadding(7);
12957 setSelectableParts(spLegendBox | spItems);
12958 setSelectedParts(spNone);
12960 setBorderPen(QPen(Qt::black));
12961 setSelectedBorderPen(QPen(Qt::blue, 2));
12962 setIconBorderPen(Qt::NoPen);
12963 setSelectedIconBorderPen(QPen(Qt::blue, 2));
12964 setBrush(Qt::white);
12965 setSelectedBrush(Qt::white);
12966 setTextColor(Qt::black);
12967 setSelectedTextColor(Qt::blue);
12970 QCPLegend::~QCPLegend()
12972 clearItems();
12973 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)
12974 mParentPlot->legendRemoved(this);
12977 /* no doc for getter, see setSelectedParts */
12978 QCPLegend::SelectableParts QCPLegend::selectedParts() const
12980 // check whether any legend elements selected, if yes, add spItems to return value
12981 bool hasSelectedItems = false;
12982 for (int i=0; i<itemCount(); ++i)
12984 if (item(i) && item(i)->selected())
12986 hasSelectedItems = true;
12987 break;
12990 if (hasSelectedItems)
12991 return mSelectedParts | spItems;
12992 else
12993 return mSelectedParts & ~spItems;
12997 Sets the pen, the border of the entire legend is drawn with.
12999 void QCPLegend::setBorderPen(const QPen &pen)
13001 mBorderPen = pen;
13005 Sets the brush of the legend background.
13007 void QCPLegend::setBrush(const QBrush &brush)
13009 mBrush = brush;
13013 Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
13014 use this font by default. However, a different font can be specified on a per-item-basis by
13015 accessing the specific legend item.
13017 This function will also set \a font on all already existing legend items.
13019 \see QCPAbstractLegendItem::setFont
13021 void QCPLegend::setFont(const QFont &font)
13023 mFont = font;
13024 for (int i=0; i<itemCount(); ++i)
13026 if (item(i))
13027 item(i)->setFont(mFont);
13032 Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
13033 will use this color by default. However, a different colors can be specified on a per-item-basis
13034 by accessing the specific legend item.
13036 This function will also set \a color on all already existing legend items.
13038 \see QCPAbstractLegendItem::setTextColor
13040 void QCPLegend::setTextColor(const QColor &color)
13042 mTextColor = color;
13043 for (int i=0; i<itemCount(); ++i)
13045 if (item(i))
13046 item(i)->setTextColor(color);
13051 Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
13052 representation of the graph) will use this size by default.
13054 void QCPLegend::setIconSize(const QSize &size)
13056 mIconSize = size;
13059 /*! \overload
13061 void QCPLegend::setIconSize(int width, int height)
13063 mIconSize.setWidth(width);
13064 mIconSize.setHeight(height);
13068 Sets the horizontal space in pixels between the legend icon and the text next to it.
13069 Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
13070 name of the graph) will use this space by default.
13072 void QCPLegend::setIconTextPadding(int padding)
13074 mIconTextPadding = padding;
13078 Sets the pen used to draw a border around each legend icon. Legend items that draw an
13079 icon (e.g. a visual representation of the graph) will use this pen by default.
13081 If no border is wanted, set this to \a Qt::NoPen.
13083 void QCPLegend::setIconBorderPen(const QPen &pen)
13085 mIconBorderPen = pen;
13089 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
13090 (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
13092 However, even when \a selectable is set to a value not allowing the selection of a specific part,
13093 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
13094 directly.
13096 \see SelectablePart, setSelectedParts
13098 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13100 if (mSelectableParts != selectable)
13102 mSelectableParts = selectable;
13103 emit selectableChanged(mSelectableParts);
13108 Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
13109 is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
13110 doesn't contain \ref spItems, those items become deselected.
13112 The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
13113 contains iSelectLegend. You only need to call this function when you wish to change the selection
13114 state manually.
13116 This function can change the selection state of a part even when \ref setSelectableParts was set to a
13117 value that actually excludes the part.
13119 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
13121 Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
13122 before, because there's no way to specify which exact items to newly select. Do this by calling
13123 \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
13125 \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
13126 setSelectedFont
13128 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13130 SelectableParts newSelected = selected;
13131 mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13133 if (mSelectedParts != newSelected)
13135 if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13137 qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13138 newSelected &= ~spItems;
13140 if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13142 for (int i=0; i<itemCount(); ++i)
13144 if (item(i))
13145 item(i)->setSelected(false);
13148 mSelectedParts = newSelected;
13149 emit selectionChanged(mSelectedParts);
13154 When the legend box is selected, this pen is used to draw the border instead of the normal pen
13155 set via \ref setBorderPen.
13157 \see setSelectedParts, setSelectableParts, setSelectedBrush
13159 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13161 mSelectedBorderPen = pen;
13165 Sets the pen legend items will use to draw their icon borders, when they are selected.
13167 \see setSelectedParts, setSelectableParts, setSelectedFont
13169 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
13171 mSelectedIconBorderPen = pen;
13175 When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
13176 set via \ref setBrush.
13178 \see setSelectedParts, setSelectableParts, setSelectedBorderPen
13180 void QCPLegend::setSelectedBrush(const QBrush &brush)
13182 mSelectedBrush = brush;
13186 Sets the default font that is used by legend items when they are selected.
13188 This function will also set \a font on all already existing legend items.
13190 \see setFont, QCPAbstractLegendItem::setSelectedFont
13192 void QCPLegend::setSelectedFont(const QFont &font)
13194 mSelectedFont = font;
13195 for (int i=0; i<itemCount(); ++i)
13197 if (item(i))
13198 item(i)->setSelectedFont(font);
13203 Sets the default text color that is used by legend items when they are selected.
13205 This function will also set \a color on all already existing legend items.
13207 \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
13209 void QCPLegend::setSelectedTextColor(const QColor &color)
13211 mSelectedTextColor = color;
13212 for (int i=0; i<itemCount(); ++i)
13214 if (item(i))
13215 item(i)->setSelectedTextColor(color);
13220 Returns the item with index \a i.
13222 \see itemCount
13224 QCPAbstractLegendItem *QCPLegend::item(int index) const
13226 return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13230 Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13231 If such an item isn't in the legend, returns 0.
13233 \see hasItemWithPlottable
13235 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
13237 for (int i=0; i<itemCount(); ++i)
13239 if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13241 if (pli->plottable() == plottable)
13242 return pli;
13245 return 0;
13249 Returns the number of items currently in the legend.
13250 \see item
13252 int QCPLegend::itemCount() const
13254 return elementCount();
13258 Returns whether the legend contains \a itm.
13260 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
13262 for (int i=0; i<itemCount(); ++i)
13264 if (item == this->item(i))
13265 return true;
13267 return false;
13271 Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13272 If such an item isn't in the legend, returns false.
13274 \see itemWithPlottable
13276 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
13278 return itemWithPlottable(plottable);
13282 Adds \a item to the legend, if it's not present already.
13284 Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
13286 The legend takes ownership of the item.
13288 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
13290 if (!hasItem(item))
13292 return addElement(rowCount(), 0, item);
13293 } else
13294 return false;
13298 Removes the item with index \a index from the legend.
13300 Returns true, if successful.
13302 \see itemCount, clearItems
13304 bool QCPLegend::removeItem(int index)
13306 if (QCPAbstractLegendItem *ali = item(index))
13308 bool success = remove(ali);
13309 simplify();
13310 return success;
13311 } else
13312 return false;
13315 /*! \overload
13317 Removes \a item from the legend.
13319 Returns true, if successful.
13321 \see clearItems
13323 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
13325 bool success = remove(item);
13326 simplify();
13327 return success;
13331 Removes all items from the legend.
13333 void QCPLegend::clearItems()
13335 for (int i=itemCount()-1; i>=0; --i)
13336 removeItem(i);
13340 Returns the legend items that are currently selected. If no items are selected,
13341 the list is empty.
13343 \see QCPAbstractLegendItem::setSelected, setSelectable
13345 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13347 QList<QCPAbstractLegendItem*> result;
13348 for (int i=0; i<itemCount(); ++i)
13350 if (QCPAbstractLegendItem *ali = item(i))
13352 if (ali->selected())
13353 result.append(ali);
13356 return result;
13359 /*! \internal
13361 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
13362 before drawing main legend elements.
13364 This is the antialiasing state the painter passed to the \ref draw method is in by default.
13366 This function takes into account the local setting of the antialiasing flag as well as the
13367 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
13368 QCustomPlot::setNotAntialiasedElements.
13370 \see setAntialiased
13372 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
13374 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
13377 /*! \internal
13379 Returns the pen used to paint the border of the legend, taking into account the selection state
13380 of the legend box.
13382 QPen QCPLegend::getBorderPen() const
13384 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13387 /*! \internal
13389 Returns the brush used to paint the background of the legend, taking into account the selection
13390 state of the legend box.
13392 QBrush QCPLegend::getBrush() const
13394 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13397 /*! \internal
13399 Draws the legend box with the provided \a painter. The individual legend items are layerables
13400 themselves, thus are drawn independently.
13402 void QCPLegend::draw(QCPPainter *painter)
13404 // draw background rect:
13405 painter->setBrush(getBrush());
13406 painter->setPen(getBorderPen());
13407 painter->drawRect(mOuterRect);
13410 /* inherits documentation from base class */
13411 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13413 if (!mParentPlot) return -1;
13414 if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13415 return -1;
13417 if (mOuterRect.contains(pos.toPoint()))
13419 if (details) details->setValue(spLegendBox);
13420 return mParentPlot->selectionTolerance()*0.99;
13422 return -1;
13425 /* inherits documentation from base class */
13426 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13428 Q_UNUSED(event)
13429 mSelectedParts = selectedParts(); // in case item selection has changed
13430 if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13432 SelectableParts selBefore = mSelectedParts;
13433 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)
13434 if (selectionStateChanged)
13435 *selectionStateChanged = mSelectedParts != selBefore;
13439 /* inherits documentation from base class */
13440 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13442 mSelectedParts = selectedParts(); // in case item selection has changed
13443 if (mSelectableParts.testFlag(spLegendBox))
13445 SelectableParts selBefore = mSelectedParts;
13446 setSelectedParts(selectedParts() & ~spLegendBox);
13447 if (selectionStateChanged)
13448 *selectionStateChanged = mSelectedParts != selBefore;
13452 /* inherits documentation from base class */
13453 QCP::Interaction QCPLegend::selectionCategory() const
13455 return QCP::iSelectLegend;
13458 /* inherits documentation from base class */
13459 QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
13461 return QCP::iSelectLegend;
13464 /* inherits documentation from base class */
13465 void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
13467 Q_UNUSED(parentPlot)
13471 ////////////////////////////////////////////////////////////////////////////////////////////////////
13472 //////////////////// QCPPlotTitle
13473 ////////////////////////////////////////////////////////////////////////////////////////////////////
13475 /*! \class QCPPlotTitle
13476 \brief A layout element displaying a plot title text
13478 The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
13479 and \ref setTextColor.
13481 A plot title can be added as follows:
13482 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation
13484 Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
13485 easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
13486 signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
13487 QCustomPlot::titleDoubleClick signal.
13490 /* start documentation of signals */
13492 /*! \fn void QCPPlotTitle::selectionChanged(bool selected)
13494 This signal is emitted when the selection state has changed to \a selected, either by user
13495 interaction or by a direct call to \ref setSelected.
13497 \see setSelected, setSelectable
13500 /* end documentation of signals */
13503 Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
13505 To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
13507 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
13508 QCPLayoutElement(parentPlot),
13509 mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
13510 mTextColor(Qt::black),
13511 mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
13512 mSelectedTextColor(Qt::blue),
13513 mSelectable(false),
13514 mSelected(false)
13516 if (parentPlot)
13518 setLayer(parentPlot->currentLayer());
13519 mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13520 mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13522 setMargins(QMargins(5, 5, 5, 0));
13525 /*! \overload
13527 Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
13529 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13530 QCPLayoutElement(parentPlot),
13531 mText(text),
13532 mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13533 mTextColor(Qt::black),
13534 mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13535 mSelectedTextColor(Qt::blue),
13536 mSelectable(false),
13537 mSelected(false)
13539 setLayer(QLatin1String("axes"));
13540 setMargins(QMargins(5, 5, 5, 0));
13544 Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
13546 \see setFont, setTextColor
13548 void QCPPlotTitle::setText(const QString &text)
13550 mText = text;
13554 Sets the \a font of the title text.
13556 \see setTextColor, setSelectedFont
13558 void QCPPlotTitle::setFont(const QFont &font)
13560 mFont = font;
13564 Sets the \a color of the title text.
13566 \see setFont, setSelectedTextColor
13568 void QCPPlotTitle::setTextColor(const QColor &color)
13570 mTextColor = color;
13574 Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
13576 \see setFont
13578 void QCPPlotTitle::setSelectedFont(const QFont &font)
13580 mSelectedFont = font;
13584 Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
13586 \see setTextColor
13588 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13590 mSelectedTextColor = color;
13594 Sets whether the user may select this plot title to \a selectable.
13596 Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
13597 programmatically via \ref setSelected.
13599 void QCPPlotTitle::setSelectable(bool selectable)
13601 if (mSelectable != selectable)
13603 mSelectable = selectable;
13604 emit selectableChanged(mSelectable);
13609 Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
13610 selectionChanged is emitted.
13612 Note that this function can change the selection state independently of the current \ref
13613 setSelectable state.
13615 void QCPPlotTitle::setSelected(bool selected)
13617 if (mSelected != selected)
13619 mSelected = selected;
13620 emit selectionChanged(mSelected);
13624 /* inherits documentation from base class */
13625 void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
13627 applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
13630 /* inherits documentation from base class */
13631 void QCPPlotTitle::draw(QCPPainter *painter)
13633 painter->setFont(mainFont());
13634 painter->setPen(QPen(mainTextColor()));
13635 painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13638 /* inherits documentation from base class */
13639 QSize QCPPlotTitle::minimumSizeHint() const
13641 QFontMetrics metrics(mFont);
13642 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13643 result.rwidth() += mMargins.left() + mMargins.right();
13644 result.rheight() += mMargins.top() + mMargins.bottom();
13645 return result;
13648 /* inherits documentation from base class */
13649 QSize QCPPlotTitle::maximumSizeHint() const
13651 QFontMetrics metrics(mFont);
13652 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13653 result.rheight() += mMargins.top() + mMargins.bottom();
13654 result.setWidth(QWIDGETSIZE_MAX);
13655 return result;
13658 /* inherits documentation from base class */
13659 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13661 Q_UNUSED(event)
13662 Q_UNUSED(details)
13663 if (mSelectable)
13665 bool selBefore = mSelected;
13666 setSelected(additive ? !mSelected : true);
13667 if (selectionStateChanged)
13668 *selectionStateChanged = mSelected != selBefore;
13672 /* inherits documentation from base class */
13673 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13675 if (mSelectable)
13677 bool selBefore = mSelected;
13678 setSelected(false);
13679 if (selectionStateChanged)
13680 *selectionStateChanged = mSelected != selBefore;
13684 /* inherits documentation from base class */
13685 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13687 Q_UNUSED(details)
13688 if (onlySelectable && !mSelectable)
13689 return -1;
13691 if (mTextBoundingRect.contains(pos.toPoint()))
13692 return mParentPlot->selectionTolerance()*0.99;
13693 else
13694 return -1;
13697 /*! \internal
13699 Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
13700 <tt>true</tt>, else mFont is returned.
13702 QFont QCPPlotTitle::mainFont() const
13704 return mSelected ? mSelectedFont : mFont;
13707 /*! \internal
13709 Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
13710 <tt>true</tt>, else mTextColor is returned.
13712 QColor QCPPlotTitle::mainTextColor() const
13714 return mSelected ? mSelectedTextColor : mTextColor;
13718 ////////////////////////////////////////////////////////////////////////////////////////////////////
13719 //////////////////// QCPColorScale
13720 ////////////////////////////////////////////////////////////////////////////////////////////////////
13722 /*! \class QCPColorScale
13723 \brief A color scale for use with color coding data such as QCPColorMap
13725 This layout element can be placed on the plot to correlate a color gradient with data values. It
13726 is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
13728 \image html QCPColorScale.png
13730 The color scale can be either horizontal or vertical, as shown in the image above. The
13731 orientation and the side where the numbers appear is controlled with \ref setType.
13733 Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
13734 connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
13735 setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
13736 scale, to make them all synchronize these properties.
13738 To have finer control over the number display and axis behaviour, you can directly access the
13739 \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
13740 you want to change the number of automatically generated ticks, call
13741 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount
13743 Placing a color scale next to the main axis rect works like with any other layout element:
13744 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation
13745 In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
13746 call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
13747 scale can be set with \ref setLabel.
13749 For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
13750 the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
13751 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup
13753 Color scales are initialized with a non-zero minimum top and bottom margin (\ref
13754 setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
13755 margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
13756 horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
13757 might want to also change the minimum margins accordingly, e.g. <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.
13760 /* start documentation of inline functions */
13762 /*! \fn QCPAxis *QCPColorScale::axis() const
13764 Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
13765 appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
13766 interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
13767 setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
13768 QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
13769 the QCPColorScale or on its QCPAxis.
13771 If the type of the color scale is changed with \ref setType, the axis returned by this method
13772 will change, too, to either the left, right, bottom or top axis, depending on which type was set.
13775 /* end documentation of signals */
13776 /* start documentation of signals */
13778 /*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
13780 This signal is emitted when the data range changes.
13782 \see setDataRange
13785 /*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
13787 This signal is emitted when the data scale type changes.
13789 \see setDataScaleType
13792 /*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
13794 This signal is emitted when the gradient changes.
13796 \see setGradient
13799 /* end documentation of signals */
13802 Constructs a new QCPColorScale.
13804 QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
13805 QCPLayoutElement(parentPlot),
13806 mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13807 mDataScaleType(QCPAxis::stLinear),
13808 mBarWidth(20),
13809 mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13811 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)
13812 setType(QCPAxis::atRight);
13813 setDataRange(QCPRange(0, 6));
13816 QCPColorScale::~QCPColorScale()
13818 delete mAxisRect;
13821 /* undocumented getter */
13822 QString QCPColorScale::label() const
13824 if (!mColorAxis)
13826 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13827 return QString();
13830 return mColorAxis.data()->label();
13833 /* undocumented getter */
13834 bool QCPColorScale::rangeDrag() const
13836 if (!mAxisRect)
13838 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13839 return false;
13842 return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13843 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13844 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13847 /* undocumented getter */
13848 bool QCPColorScale::rangeZoom() const
13850 if (!mAxisRect)
13852 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13853 return false;
13856 return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13857 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13858 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13862 Sets at which side of the color scale the axis is placed, and thus also its orientation.
13864 Note that after setting \a type to a different value, the axis returned by \ref axis() will
13865 be a different one. The new axis will adopt the following properties from the previous axis: The
13866 range, scale type, log base and label.
13868 void QCPColorScale::setType(QCPAxis::AxisType type)
13870 if (!mAxisRect)
13872 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13873 return;
13875 if (mType != type)
13877 mType = type;
13878 QCPRange rangeTransfer(0, 6);
13879 double logBaseTransfer = 10;
13880 QString labelTransfer;
13881 // revert some settings on old axis:
13882 if (mColorAxis)
13884 rangeTransfer = mColorAxis.data()->range();
13885 labelTransfer = mColorAxis.data()->label();
13886 logBaseTransfer = mColorAxis.data()->scaleLogBase();
13887 mColorAxis.data()->setLabel(QString());
13888 disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13889 disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13891 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
13892 foreach (QCPAxis::AxisType atype, allAxisTypes)
13894 mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13895 mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13897 // set new mColorAxis pointer:
13898 mColorAxis = mAxisRect.data()->axis(mType);
13899 // transfer settings to new axis:
13900 mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13901 mColorAxis.data()->setLabel(labelTransfer);
13902 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
13903 connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13904 connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13905 mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13906 QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13911 Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
13913 It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
13914 also equivalent to directly accessing the \ref axis and setting its range with \ref
13915 QCPAxis::setRange.
13917 \see setDataScaleType, setGradient, rescaleDataRange
13919 void QCPColorScale::setDataRange(const QCPRange &dataRange)
13921 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13923 mDataRange = dataRange;
13924 if (mColorAxis)
13925 mColorAxis.data()->setRange(mDataRange);
13926 emit dataRangeChanged(mDataRange);
13931 Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
13932 or logarithmically.
13934 It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
13935 also equivalent to directly accessing the \ref axis and setting its scale type with \ref
13936 QCPAxis::setScaleType.
13938 \see setDataRange, setGradient
13940 void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
13942 if (mDataScaleType != scaleType)
13944 mDataScaleType = scaleType;
13945 if (mColorAxis)
13946 mColorAxis.data()->setScaleType(mDataScaleType);
13947 if (mDataScaleType == QCPAxis::stLogarithmic)
13948 setDataRange(mDataRange.sanitizedForLogScale());
13949 emit dataScaleTypeChanged(mDataScaleType);
13954 Sets the color gradient that will be used to represent data values.
13956 It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
13958 \see setDataRange, setDataScaleType
13960 void QCPColorScale::setGradient(const QCPColorGradient &gradient)
13962 if (mGradient != gradient)
13964 mGradient = gradient;
13965 if (mAxisRect)
13966 mAxisRect.data()->mGradientImageInvalidated = true;
13967 emit gradientChanged(mGradient);
13972 Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
13973 the internal \ref axis.
13975 void QCPColorScale::setLabel(const QString &str)
13977 if (!mColorAxis)
13979 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13980 return;
13983 mColorAxis.data()->setLabel(str);
13987 Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
13988 will have.
13990 void QCPColorScale::setBarWidth(int width)
13992 mBarWidth = width;
13996 Sets whether the user can drag the data range (\ref setDataRange).
13998 Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
13999 QCustomPlot::setInteractions) to allow range dragging.
14001 void QCPColorScale::setRangeDrag(bool enabled)
14003 if (!mAxisRect)
14005 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14006 return;
14009 if (enabled)
14010 mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14011 else
14012 mAxisRect.data()->setRangeDrag(0);
14016 Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
14018 Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
14019 QCustomPlot::setInteractions) to allow range dragging.
14021 void QCPColorScale::setRangeZoom(bool enabled)
14023 if (!mAxisRect)
14025 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14026 return;
14029 if (enabled)
14030 mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14031 else
14032 mAxisRect.data()->setRangeZoom(0);
14036 Returns a list of all the color maps associated with this color scale.
14038 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14040 QList<QCPColorMap*> result;
14041 for (int i=0; i<mParentPlot->plottableCount(); ++i)
14043 if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14044 if (cm->colorScale() == this)
14045 result.append(cm);
14047 return result;
14051 Changes the data range such that all color maps associated with this color scale are fully mapped
14052 to the gradient in the data dimension.
14054 \see setDataRange
14056 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14058 QList<QCPColorMap*> maps = colorMaps();
14059 QCPRange newRange;
14060 bool haveRange = false;
14061 int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14062 if (mDataScaleType == QCPAxis::stLogarithmic)
14063 sign = (mDataRange.upper < 0 ? -1 : 1);
14064 for (int i=0; i<maps.size(); ++i)
14066 if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14067 continue;
14068 QCPRange mapRange;
14069 if (maps.at(i)->colorScale() == this)
14071 bool currentFoundRange = true;
14072 mapRange = maps.at(i)->data()->dataBounds();
14073 if (sign == 1)
14075 if (mapRange.lower <= 0 && mapRange.upper > 0)
14076 mapRange.lower = mapRange.upper*1e-3;
14077 else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14078 currentFoundRange = false;
14079 } else if (sign == -1)
14081 if (mapRange.upper >= 0 && mapRange.lower < 0)
14082 mapRange.upper = mapRange.lower*1e-3;
14083 else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14084 currentFoundRange = false;
14086 if (currentFoundRange)
14088 if (!haveRange)
14089 newRange = mapRange;
14090 else
14091 newRange.expand(mapRange);
14092 haveRange = true;
14096 if (haveRange)
14098 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
14100 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
14101 if (mDataScaleType == QCPAxis::stLinear)
14103 newRange.lower = center-mDataRange.size()/2.0;
14104 newRange.upper = center+mDataRange.size()/2.0;
14105 } else // mScaleType == stLogarithmic
14107 newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14108 newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14111 setDataRange(newRange);
14115 /* inherits documentation from base class */
14116 void QCPColorScale::update(UpdatePhase phase)
14118 QCPLayoutElement::update(phase);
14119 if (!mAxisRect)
14121 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14122 return;
14125 mAxisRect.data()->update(phase);
14127 switch (phase)
14129 case upMargins:
14131 if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
14133 setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14134 setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14135 } else
14137 setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14138 setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14140 break;
14142 case upLayout:
14144 mAxisRect.data()->setOuterRect(rect());
14145 break;
14147 default: break;
14151 /* inherits documentation from base class */
14152 void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
14154 painter->setAntialiasing(false);
14157 /* inherits documentation from base class */
14158 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14160 if (!mAxisRect)
14162 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14163 return;
14165 mAxisRect.data()->mousePressEvent(event);
14168 /* inherits documentation from base class */
14169 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14171 if (!mAxisRect)
14173 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14174 return;
14176 mAxisRect.data()->mouseMoveEvent(event);
14179 /* inherits documentation from base class */
14180 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14182 if (!mAxisRect)
14184 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14185 return;
14187 mAxisRect.data()->mouseReleaseEvent(event);
14190 /* inherits documentation from base class */
14191 void QCPColorScale::wheelEvent(QWheelEvent *event)
14193 if (!mAxisRect)
14195 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14196 return;
14198 mAxisRect.data()->wheelEvent(event);
14201 ////////////////////////////////////////////////////////////////////////////////////////////////////
14202 //////////////////// QCPColorScaleAxisRectPrivate
14203 ////////////////////////////////////////////////////////////////////////////////////////////////////
14205 /*! \class QCPColorScaleAxisRectPrivate
14207 \internal
14208 \brief An axis rect subclass for use in a QCPColorScale
14210 This is a private class and not part of the public QCustomPlot interface.
14212 It provides the axis rect functionality for the QCPColorScale class.
14217 Creates a new instance, as a child of \a parentColorScale.
14219 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
14220 QCPAxisRect(parentColorScale->parentPlot(), true),
14221 mParentColorScale(parentColorScale),
14222 mGradientImageInvalidated(true)
14224 setParentLayerable(parentColorScale);
14225 setMinimumMargins(QMargins(0, 0, 0, 0));
14226 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14227 foreach (QCPAxis::AxisType type, allAxisTypes)
14229 axis(type)->setVisible(true);
14230 axis(type)->grid()->setVisible(false);
14231 axis(type)->setPadding(0);
14232 connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14233 connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14236 connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14237 connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14238 connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14239 connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14240 connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14241 connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14242 connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14243 connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14245 // make layer transfers of color scale transfer to axis rect and axes
14246 // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14247 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14248 foreach (QCPAxis::AxisType type, allAxisTypes)
14249 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14252 /*! \internal
14253 Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
14254 it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
14256 void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
14258 if (mGradientImageInvalidated)
14259 updateGradientImage();
14261 bool mirrorHorz = false;
14262 bool mirrorVert = false;
14263 if (mParentColorScale->mColorAxis)
14265 mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14266 mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14269 painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14270 QCPAxisRect::draw(painter);
14273 /*! \internal
14275 Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
14276 generate a gradient image. This gradient image will be used in the \ref draw method.
14278 void QCPColorScaleAxisRectPrivate::updateGradientImage()
14280 if (rect().isEmpty())
14281 return;
14283 int n = mParentColorScale->mGradient.levelCount();
14284 int w, h;
14285 QVector<double> data(n);
14286 for (int i=0; i<n; ++i)
14287 data[i] = i;
14288 if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
14290 w = n;
14291 h = rect().height();
14292 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14293 QVector<QRgb*> pixels;
14294 for (int y=0; y<h; ++y)
14295 pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14296 mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14297 for (int y=1; y<h; ++y)
14298 memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14299 } else
14301 w = rect().width();
14302 h = n;
14303 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14304 for (int y=0; y<h; ++y)
14306 QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14307 const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14308 for (int x=0; x<w; ++x)
14309 pixels[x] = lineColor;
14312 mGradientImageInvalidated = false;
14315 /*! \internal
14317 This slot is connected to the selectionChanged signals of the four axes in the constructor. It
14318 synchronizes the selection state of the axes.
14320 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14322 // axis bases of four axes shall always (de-)selected synchronously:
14323 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14324 foreach (QCPAxis::AxisType type, allAxisTypes)
14326 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14327 if (senderAxis->axisType() == type)
14328 continue;
14330 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14332 if (selectedParts.testFlag(QCPAxis::spAxis))
14333 axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14334 else
14335 axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14340 /*! \internal
14342 This slot is connected to the selectableChanged signals of the four axes in the constructor. It
14343 synchronizes the selectability of the axes.
14345 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14347 // synchronize axis base selectability:
14348 QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
14349 foreach (QCPAxis::AxisType type, allAxisTypes)
14351 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14352 if (senderAxis->axisType() == type)
14353 continue;
14355 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14357 if (selectableParts.testFlag(QCPAxis::spAxis))
14358 axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14359 else
14360 axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14366 ////////////////////////////////////////////////////////////////////////////////////////////////////
14367 //////////////////// QCPData
14368 ////////////////////////////////////////////////////////////////////////////////////////////////////
14370 /*! \class QCPData
14371 \brief Holds the data of one single data point for QCPGraph.
14373 The container for storing multiple data points is \ref QCPDataMap.
14375 The stored data is:
14376 \li \a key: coordinate on the key axis of this data point
14377 \li \a value: coordinate on the value axis of this data point
14378 \li \a keyErrorMinus: negative error in the key dimension (for error bars)
14379 \li \a keyErrorPlus: positive error in the key dimension (for error bars)
14380 \li \a valueErrorMinus: negative error in the value dimension (for error bars)
14381 \li \a valueErrorPlus: positive error in the value dimension (for error bars)
14383 \see QCPDataMap
14387 Constructs a data point with key, value and all errors set to zero.
14389 QCPData::QCPData() :
14390 key(0),
14391 value(0),
14392 keyErrorPlus(0),
14393 keyErrorMinus(0),
14394 valueErrorPlus(0),
14395 valueErrorMinus(0)
14400 Constructs a data point with the specified \a key and \a value. All errors are set to zero.
14402 QCPData::QCPData(double key, double value) :
14403 key(key),
14404 value(value),
14405 keyErrorPlus(0),
14406 keyErrorMinus(0),
14407 valueErrorPlus(0),
14408 valueErrorMinus(0)
14413 ////////////////////////////////////////////////////////////////////////////////////////////////////
14414 //////////////////// QCPGraph
14415 ////////////////////////////////////////////////////////////////////////////////////////////////////
14417 /*! \class QCPGraph
14418 \brief A plottable representing a graph in a plot.
14420 \image html QCPGraph.png
14422 Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
14423 instance is accessed via QCustomPlot::graph.
14425 To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
14426 also access and modify the graph's data via the \ref data method, which returns a pointer to the
14427 internal \ref QCPDataMap.
14429 Graphs are used to display single-valued data. Single-valued means that there should only be one
14430 data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
14431 want to plot non-single-valued curves, rather use the QCPCurve plottable.
14433 Gaps in the graph line can be created by adding data points with NaN as value
14434 (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
14435 separated.
14437 \section appearance Changing the appearance
14439 The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
14440 of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
14442 \subsection filling Filling under or between graphs
14444 QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
14445 the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
14446 just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
14448 By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
14449 between this graph and another one, call \ref setChannelFillGraph with the other graph as
14450 parameter.
14452 \see QCustomPlot::addGraph, QCustomPlot::graph
14455 /* start of documentation of inline functions */
14457 /*! \fn QCPDataMap *QCPGraph::data() const
14459 Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
14460 directly manipulate the data, which may be more convenient and faster than using the regular \ref
14461 setData or \ref addData methods, in certain situations.
14464 /* end of documentation of inline functions */
14467 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
14468 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
14469 the same orientation. If either of these restrictions is violated, a corresponding message is
14470 printed to the debug output (qDebug), the construction is not aborted, though.
14472 The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
14473 then takes ownership of the graph.
14475 To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
14477 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14478 QCPAbstractPlottable(keyAxis, valueAxis)
14480 mData = new QCPDataMap;
14482 setPen(QPen(Qt::blue, 0));
14483 setErrorPen(QPen(Qt::black));
14484 setBrush(Qt::NoBrush);
14485 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14486 setSelectedBrush(Qt::NoBrush);
14488 setLineStyle(lsLine);
14489 setErrorType(etNone);
14490 setErrorBarSize(6);
14491 setErrorBarSkipSymbol(true);
14492 setChannelFillGraph(0);
14493 setAdaptiveSampling(true);
14496 QCPGraph::~QCPGraph()
14498 delete mData;
14502 Replaces the current data with the provided \a data.
14504 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
14505 takes ownership of the passed data and replaces the internal data pointer with it. This is
14506 significantly faster than copying for large datasets.
14508 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14509 returns a pointer to the internal \ref QCPDataMap.
14511 void QCPGraph::setData(QCPDataMap *data, bool copy)
14513 if (mData == data)
14515 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
14516 return;
14518 if (copy)
14520 *mData = *data;
14521 } else
14523 delete mData;
14524 mData = data;
14528 /*! \overload
14530 Replaces the current data with the provided points in \a key and \a value pairs. The provided
14531 vectors should have equal length. Else, the number of added points will be the size of the
14532 smallest vector.
14534 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14536 mData->clear();
14537 int n = key.size();
14538 n = qMin(n, value.size());
14539 QCPData newData;
14540 for (int i=0; i<n; ++i)
14542 newData.key = key[i];
14543 newData.value = value[i];
14544 mData->insertMulti(newData.key, newData);
14549 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14550 symmetrical value error of the data points are set to the values in \a valueError.
14551 For error bars to show appropriately, see \ref setErrorType.
14552 The provided vectors should have equal length. Else, the number of added points will be the size of the
14553 smallest vector.
14555 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14557 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14559 mData->clear();
14560 int n = key.size();
14561 n = qMin(n, value.size());
14562 n = qMin(n, valueError.size());
14563 QCPData newData;
14564 for (int i=0; i<n; ++i)
14566 newData.key = key[i];
14567 newData.value = value[i];
14568 newData.valueErrorMinus = valueError[i];
14569 newData.valueErrorPlus = valueError[i];
14570 mData->insertMulti(key[i], newData);
14575 \overload
14576 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14577 negative value error of the data points are set to the values in \a valueErrorMinus, the positive
14578 value error to \a valueErrorPlus.
14579 For error bars to show appropriately, see \ref setErrorType.
14580 The provided vectors should have equal length. Else, the number of added points will be the size of the
14581 smallest vector.
14583 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14585 mData->clear();
14586 int n = key.size();
14587 n = qMin(n, value.size());
14588 n = qMin(n, valueErrorMinus.size());
14589 n = qMin(n, valueErrorPlus.size());
14590 QCPData newData;
14591 for (int i=0; i<n; ++i)
14593 newData.key = key[i];
14594 newData.value = value[i];
14595 newData.valueErrorMinus = valueErrorMinus[i];
14596 newData.valueErrorPlus = valueErrorPlus[i];
14597 mData->insertMulti(key[i], newData);
14602 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14603 symmetrical key error of the data points are set to the values in \a keyError.
14604 For error bars to show appropriately, see \ref setErrorType.
14605 The provided vectors should have equal length. Else, the number of added points will be the size of the
14606 smallest vector.
14608 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14610 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14612 mData->clear();
14613 int n = key.size();
14614 n = qMin(n, value.size());
14615 n = qMin(n, keyError.size());
14616 QCPData newData;
14617 for (int i=0; i<n; ++i)
14619 newData.key = key[i];
14620 newData.value = value[i];
14621 newData.keyErrorMinus = keyError[i];
14622 newData.keyErrorPlus = keyError[i];
14623 mData->insertMulti(key[i], newData);
14628 \overload
14629 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14630 negative key error of the data points are set to the values in \a keyErrorMinus, the positive
14631 key error to \a keyErrorPlus.
14632 For error bars to show appropriately, see \ref setErrorType.
14633 The provided vectors should have equal length. Else, the number of added points will be the size of the
14634 smallest vector.
14636 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14638 mData->clear();
14639 int n = key.size();
14640 n = qMin(n, value.size());
14641 n = qMin(n, keyErrorMinus.size());
14642 n = qMin(n, keyErrorPlus.size());
14643 QCPData newData;
14644 for (int i=0; i<n; ++i)
14646 newData.key = key[i];
14647 newData.value = value[i];
14648 newData.keyErrorMinus = keyErrorMinus[i];
14649 newData.keyErrorPlus = keyErrorPlus[i];
14650 mData->insertMulti(key[i], newData);
14655 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14656 symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
14657 For error bars to show appropriately, see \ref setErrorType.
14658 The provided vectors should have equal length. Else, the number of added points will be the size of the
14659 smallest vector.
14661 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14663 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14665 mData->clear();
14666 int n = key.size();
14667 n = qMin(n, value.size());
14668 n = qMin(n, valueError.size());
14669 n = qMin(n, keyError.size());
14670 QCPData newData;
14671 for (int i=0; i<n; ++i)
14673 newData.key = key[i];
14674 newData.value = value[i];
14675 newData.keyErrorMinus = keyError[i];
14676 newData.keyErrorPlus = keyError[i];
14677 newData.valueErrorMinus = valueError[i];
14678 newData.valueErrorPlus = valueError[i];
14679 mData->insertMulti(key[i], newData);
14684 \overload
14685 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14686 negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
14687 key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
14688 For error bars to show appropriately, see \ref setErrorType.
14689 The provided vectors should have equal length. Else, the number of added points will be the size of the
14690 smallest vector.
14692 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)
14694 mData->clear();
14695 int n = key.size();
14696 n = qMin(n, value.size());
14697 n = qMin(n, valueErrorMinus.size());
14698 n = qMin(n, valueErrorPlus.size());
14699 n = qMin(n, keyErrorMinus.size());
14700 n = qMin(n, keyErrorPlus.size());
14701 QCPData newData;
14702 for (int i=0; i<n; ++i)
14704 newData.key = key[i];
14705 newData.value = value[i];
14706 newData.keyErrorMinus = keyErrorMinus[i];
14707 newData.keyErrorPlus = keyErrorPlus[i];
14708 newData.valueErrorMinus = valueErrorMinus[i];
14709 newData.valueErrorPlus = valueErrorPlus[i];
14710 mData->insertMulti(key[i], newData);
14716 Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to
14717 \ref lsNone and \ref setScatterStyle to the desired scatter style.
14719 \see setScatterStyle
14721 void QCPGraph::setLineStyle(LineStyle ls)
14723 mLineStyle = ls;
14727 Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
14728 are drawn (e.g. for line-only-plots with appropriate line style).
14730 \see QCPScatterStyle, setLineStyle
14732 void QCPGraph::setScatterStyle(const QCPScatterStyle &style)
14734 mScatterStyle = style;
14738 Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
14739 point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
14740 error data via the specific setData functions along with the data points (e.g. \ref
14741 setDataValueError, \ref setDataKeyError, \ref setDataBothError).
14743 \see ErrorType
14745 void QCPGraph::setErrorType(ErrorType errorType)
14747 mErrorType = errorType;
14751 Sets the pen with which the error bars will be drawn.
14752 \see setErrorBarSize, setErrorType
14754 void QCPGraph::setErrorPen(const QPen &pen)
14756 mErrorPen = pen;
14760 Sets the width of the handles at both ends of an error bar in pixels.
14762 void QCPGraph::setErrorBarSize(double size)
14764 mErrorBarSize = size;
14768 If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
14769 leave some free space around the symbol.
14771 This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to determine the size
14772 of the area to leave blank. So when drawing Pixmaps as scatter points (\ref
14773 QCPScatterStyle::ssPixmap), the scatter size must be set manually to a value corresponding to the
14774 size of the Pixmap, if the error bars should leave gaps to its boundaries.
14776 \ref setErrorType, setErrorBarSize, setScatterStyle
14778 void QCPGraph::setErrorBarSkipSymbol(bool enabled)
14780 mErrorBarSkipSymbol = enabled;
14784 Sets the target graph for filling the area between this graph and \a targetGraph with the current
14785 brush (\ref setBrush).
14787 When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To
14788 disable any filling, set the brush to Qt::NoBrush.
14790 \see setBrush
14792 void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
14794 // prevent setting channel target to this graph itself:
14795 if (targetGraph == this)
14797 qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14798 mChannelFillGraph = 0;
14799 return;
14801 // prevent setting channel target to a graph not in the plot:
14802 if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14804 qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14805 mChannelFillGraph = 0;
14806 return;
14809 mChannelFillGraph = targetGraph;
14813 Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
14814 sampling technique can drastically improve the replot performance for graphs with a larger number
14815 of points (e.g. above 10,000), without notably changing the appearance of the graph.
14817 By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
14818 sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
14819 disadvantage in almost all cases.
14821 \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
14823 As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
14824 reproduced reliably, as well as the overall shape of the data set. The replot time reduces
14825 dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
14827 \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
14829 Care must be taken when using high-density scatter plots in combination with adaptive sampling.
14830 The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
14831 gives a significant reduction of replot times, but not quite as much as for line plots. This is
14832 because scatter plots inherently need more data points to be preserved in order to still resemble
14833 the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
14834 identical, as banding occurs for the outer data points. This is in fact intentional, such that
14835 the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
14836 depends on the point density, i.e. the number of points in the plot.
14838 For some situations with scatter plots it might thus be desirable to manually turn adaptive
14839 sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
14840 enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
14841 back to true afterwards.
14843 void QCPGraph::setAdaptiveSampling(bool enabled)
14845 mAdaptiveSampling = enabled;
14849 Adds the provided data points in \a dataMap to the current data.
14851 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14852 returns a pointer to the internal \ref QCPDataMap.
14854 \see removeData
14856 void QCPGraph::addData(const QCPDataMap &dataMap)
14858 mData->unite(dataMap);
14861 /*! \overload
14862 Adds the provided single data point in \a data to the current data.
14864 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14865 returns a pointer to the internal \ref QCPDataMap.
14867 \see removeData
14869 void QCPGraph::addData(const QCPData &data)
14871 mData->insertMulti(data.key, data);
14874 /*! \overload
14875 Adds the provided single data point as \a key and \a value pair to the current data.
14877 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14878 returns a pointer to the internal \ref QCPDataMap.
14880 \see removeData
14882 void QCPGraph::addData(double key, double value)
14884 QCPData newData;
14885 newData.key = key;
14886 newData.value = value;
14887 mData->insertMulti(newData.key, newData);
14890 /*! \overload
14891 Adds the provided data points as \a key and \a value pairs to the current data.
14893 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14894 returns a pointer to the internal \ref QCPDataMap.
14896 \see removeData
14898 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14900 int n = qMin(keys.size(), values.size());
14901 QCPData newData;
14902 for (int i=0; i<n; ++i)
14904 newData.key = keys[i];
14905 newData.value = values[i];
14906 mData->insertMulti(newData.key, newData);
14911 Removes all data points with keys smaller than \a key.
14912 \see addData, clearData
14914 void QCPGraph::removeDataBefore(double key)
14916 QCPDataMap::iterator it = mData->begin();
14917 while (it != mData->end() && it.key() < key)
14918 it = mData->erase(it);
14922 Removes all data points with keys greater than \a key.
14923 \see addData, clearData
14925 void QCPGraph::removeDataAfter(double key)
14927 if (mData->isEmpty()) return;
14928 QCPDataMap::iterator it = mData->upperBound(key);
14929 while (it != mData->end())
14930 it = mData->erase(it);
14934 Removes all data points with keys between \a fromKey and \a toKey.
14935 if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
14936 a single data point with known key, use \ref removeData(double key).
14938 \see addData, clearData
14940 void QCPGraph::removeData(double fromKey, double toKey)
14942 if (fromKey >= toKey || mData->isEmpty()) return;
14943 QCPDataMap::iterator it = mData->upperBound(fromKey);
14944 QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14945 while (it != itEnd)
14946 it = mData->erase(it);
14949 /*! \overload
14951 Removes a single data point at \a key. If the position is not known with absolute precision,
14952 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
14953 the suspected position, depeding on the precision with which the key is known.
14955 \see addData, clearData
14957 void QCPGraph::removeData(double key)
14959 mData->remove(key);
14963 Removes all data points.
14964 \see removeData, removeDataAfter, removeDataBefore
14966 void QCPGraph::clearData()
14968 mData->clear();
14971 /* inherits documentation from base class */
14972 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14974 Q_UNUSED(details)
14975 if ((onlySelectable && !mSelectable) || mData->isEmpty())
14976 return -1;
14977 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14979 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
14980 return pointDistance(pos);
14981 else
14982 return -1;
14985 /*! \overload
14987 Allows to define whether error bars are taken into consideration when determining the new axis
14988 range.
14990 \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
14992 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
14994 rescaleKeyAxis(onlyEnlarge, includeErrorBars);
14995 rescaleValueAxis(onlyEnlarge, includeErrorBars);
14998 /*! \overload
15000 Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
15001 when determining the new axis range.
15003 \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis
15005 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15007 // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15008 // that getKeyRange is passed the includeErrorBars value.
15009 if (mData->isEmpty()) return;
15011 QCPAxis *keyAxis = mKeyAxis.data();
15012 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15014 SignDomain signDomain = sdBoth;
15015 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15016 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15018 bool foundRange;
15019 QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15021 if (foundRange)
15023 if (onlyEnlarge)
15025 if (keyAxis->range().lower < newRange.lower)
15026 newRange.lower = keyAxis->range().lower;
15027 if (keyAxis->range().upper > newRange.upper)
15028 newRange.upper = keyAxis->range().upper;
15030 keyAxis->setRange(newRange);
15034 /*! \overload
15036 Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
15037 when determining the new axis range.
15039 \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis
15041 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15043 // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15044 // is that getValueRange is passed the includeErrorBars value.
15045 if (mData->isEmpty()) return;
15047 QCPAxis *valueAxis = mValueAxis.data();
15048 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15050 SignDomain signDomain = sdBoth;
15051 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15052 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15054 bool foundRange;
15055 QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15057 if (foundRange)
15059 if (onlyEnlarge)
15061 if (valueAxis->range().lower < newRange.lower)
15062 newRange.lower = valueAxis->range().lower;
15063 if (valueAxis->range().upper > newRange.upper)
15064 newRange.upper = valueAxis->range().upper;
15066 valueAxis->setRange(newRange);
15070 /* inherits documentation from base class */
15071 void QCPGraph::draw(QCPPainter *painter)
15073 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15074 if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15075 if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15077 // allocate line and (if necessary) point vectors:
15078 QVector<QPointF> *lineData = new QVector<QPointF>;
15079 QVector<QCPData> *scatterData = 0;
15080 if (!mScatterStyle.isNone())
15081 scatterData = new QVector<QCPData>;
15083 // fill vectors with data appropriate to plot style:
15084 getPlotData(lineData, scatterData);
15086 // check data validity if flag set:
15087 #ifdef QCUSTOMPLOT_CHECK_DATA
15088 QCPDataMap::const_iterator it;
15089 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15091 if (QCP::isInvalidData(it.value().key, it.value().value) ||
15092 QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15093 QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15094 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15096 #endif
15098 // draw fill of graph:
15099 drawFill(painter, lineData);
15101 // draw line:
15102 if (mLineStyle == lsImpulse)
15103 drawImpulsePlot(painter, lineData);
15104 else if (mLineStyle != lsNone)
15105 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15107 // draw scatters:
15108 if (scatterData)
15109 drawScatterPlot(painter, scatterData);
15111 // free allocated line and point vectors:
15112 delete lineData;
15113 if (scatterData)
15114 delete scatterData;
15117 /* inherits documentation from base class */
15118 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15120 // draw fill:
15121 if (mBrush.style() != Qt::NoBrush)
15123 applyFillAntialiasingHint(painter);
15124 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15126 // draw line vertically centered:
15127 if (mLineStyle != lsNone)
15129 applyDefaultAntialiasingHint(painter);
15130 painter->setPen(mPen);
15131 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
15133 // draw scatter symbol:
15134 if (!mScatterStyle.isNone())
15136 applyScattersAntialiasingHint(painter);
15137 // scale scatter pixmap if it's too large to fit in legend icon rect:
15138 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15140 QCPScatterStyle scaledStyle(mScatterStyle);
15141 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15142 scaledStyle.applyTo(painter, mPen);
15143 scaledStyle.drawShape(painter, QRectF(rect).center());
15144 } else
15146 mScatterStyle.applyTo(painter, mPen);
15147 mScatterStyle.drawShape(painter, QRectF(rect).center());
15152 /*! \internal
15154 This function branches out to the line style specific "get(...)PlotData" functions, according to
15155 the line style of the graph.
15157 \a lineData will be filled with raw points that will be drawn with the according draw functions,
15158 e.g. \ref drawLinePlot and \ref drawImpulsePlot. These aren't necessarily the original data
15159 points, since for step plots for example, additional points are needed for drawing lines that
15160 make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
15161 untouched.
15163 \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
15164 scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
15165 \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
15167 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
15168 getStepCenterPlotData, getImpulsePlotData
15170 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15172 switch(mLineStyle)
15174 case lsNone: getScatterPlotData(scatterData); break;
15175 case lsLine: getLinePlotData(lineData, scatterData); break;
15176 case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15177 case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15178 case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15179 case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15183 /*! \internal
15185 If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
15186 this function serves at providing the visible data points in \a scatterData, so the \ref
15187 drawScatterPlot function can draw the scatter points accordingly.
15189 If line style is not \ref lsNone, this function is not called and the data for the scatter points
15190 are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
15192 \see drawScatterPlot
15194 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15196 getPreparedData(0, scatterData);
15199 /*! \internal
15201 Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
15203 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15204 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15205 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15206 scatterData, and the function will skip filling the vector.
15208 \see drawLinePlot
15210 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15212 QCPAxis *keyAxis = mKeyAxis.data();
15213 QCPAxis *valueAxis = mValueAxis.data();
15214 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15215 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15217 QVector<QCPData> lineData;
15218 getPreparedData(&lineData, scatterData);
15219 linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15220 linePixelData->resize(lineData.size());
15222 // transform lineData points to pixels:
15223 if (keyAxis->orientation() == Qt::Vertical)
15225 for (int i=0; i<lineData.size(); ++i)
15227 (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15228 (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15230 } else // key axis is horizontal
15232 for (int i=0; i<lineData.size(); ++i)
15234 (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15235 (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15241 \internal
15242 Places the raw data points needed for a step plot with left oriented steps in \a lineData.
15244 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15245 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15246 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15247 scatterData, and the function will skip filling the vector.
15249 \see drawLinePlot
15251 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15253 QCPAxis *keyAxis = mKeyAxis.data();
15254 QCPAxis *valueAxis = mValueAxis.data();
15255 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15256 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15258 QVector<QCPData> lineData;
15259 getPreparedData(&lineData, scatterData);
15260 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15261 linePixelData->resize(lineData.size()*2);
15263 // calculate steps from lineData and transform to pixel coordinates:
15264 if (keyAxis->orientation() == Qt::Vertical)
15266 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15267 double key;
15268 for (int i=0; i<lineData.size(); ++i)
15270 key = keyAxis->coordToPixel(lineData.at(i).key);
15271 (*linePixelData)[i*2+0].setX(lastValue);
15272 (*linePixelData)[i*2+0].setY(key);
15273 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15274 (*linePixelData)[i*2+1].setX(lastValue);
15275 (*linePixelData)[i*2+1].setY(key);
15277 } else // key axis is horizontal
15279 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15280 double key;
15281 for (int i=0; i<lineData.size(); ++i)
15283 key = keyAxis->coordToPixel(lineData.at(i).key);
15284 (*linePixelData)[i*2+0].setX(key);
15285 (*linePixelData)[i*2+0].setY(lastValue);
15286 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15287 (*linePixelData)[i*2+1].setX(key);
15288 (*linePixelData)[i*2+1].setY(lastValue);
15294 \internal
15295 Places the raw data points needed for a step plot with right oriented steps in \a lineData.
15297 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15298 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15299 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15300 scatterData, and the function will skip filling the vector.
15302 \see drawLinePlot
15304 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15306 QCPAxis *keyAxis = mKeyAxis.data();
15307 QCPAxis *valueAxis = mValueAxis.data();
15308 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15309 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15311 QVector<QCPData> lineData;
15312 getPreparedData(&lineData, scatterData);
15313 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15314 linePixelData->resize(lineData.size()*2);
15316 // calculate steps from lineData and transform to pixel coordinates:
15317 if (keyAxis->orientation() == Qt::Vertical)
15319 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15320 double value;
15321 for (int i=0; i<lineData.size(); ++i)
15323 value = valueAxis->coordToPixel(lineData.at(i).value);
15324 (*linePixelData)[i*2+0].setX(value);
15325 (*linePixelData)[i*2+0].setY(lastKey);
15326 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15327 (*linePixelData)[i*2+1].setX(value);
15328 (*linePixelData)[i*2+1].setY(lastKey);
15330 } else // key axis is horizontal
15332 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15333 double value;
15334 for (int i=0; i<lineData.size(); ++i)
15336 value = valueAxis->coordToPixel(lineData.at(i).value);
15337 (*linePixelData)[i*2+0].setX(lastKey);
15338 (*linePixelData)[i*2+0].setY(value);
15339 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15340 (*linePixelData)[i*2+1].setX(lastKey);
15341 (*linePixelData)[i*2+1].setY(value);
15347 \internal
15348 Places the raw data points needed for a step plot with centered steps in \a lineData.
15350 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15351 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15352 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15353 scatterData, and the function will skip filling the vector.
15355 \see drawLinePlot
15357 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15359 QCPAxis *keyAxis = mKeyAxis.data();
15360 QCPAxis *valueAxis = mValueAxis.data();
15361 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15362 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15364 QVector<QCPData> lineData;
15365 getPreparedData(&lineData, scatterData);
15366 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15367 linePixelData->resize(lineData.size()*2);
15368 // calculate steps from lineData and transform to pixel coordinates:
15369 if (keyAxis->orientation() == Qt::Vertical)
15371 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15372 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15373 double key;
15374 (*linePixelData)[0].setX(lastValue);
15375 (*linePixelData)[0].setY(lastKey);
15376 for (int i=1; i<lineData.size(); ++i)
15378 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15379 (*linePixelData)[i*2-1].setX(lastValue);
15380 (*linePixelData)[i*2-1].setY(key);
15381 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15382 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15383 (*linePixelData)[i*2+0].setX(lastValue);
15384 (*linePixelData)[i*2+0].setY(key);
15386 (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15387 (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15388 } else // key axis is horizontal
15390 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15391 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15392 double key;
15393 (*linePixelData)[0].setX(lastKey);
15394 (*linePixelData)[0].setY(lastValue);
15395 for (int i=1; i<lineData.size(); ++i)
15397 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15398 (*linePixelData)[i*2-1].setX(key);
15399 (*linePixelData)[i*2-1].setY(lastValue);
15400 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15401 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15402 (*linePixelData)[i*2+0].setX(key);
15403 (*linePixelData)[i*2+0].setY(lastValue);
15405 (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15406 (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15412 \internal
15413 Places the raw data points needed for an impulse plot in \a lineData.
15415 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15416 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15417 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15418 scatterData, and the function will skip filling the vector.
15420 \see drawImpulsePlot
15422 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15424 QCPAxis *keyAxis = mKeyAxis.data();
15425 QCPAxis *valueAxis = mValueAxis.data();
15426 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15427 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15429 QVector<QCPData> lineData;
15430 getPreparedData(&lineData, scatterData);
15431 linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15433 // transform lineData points to pixels:
15434 if (keyAxis->orientation() == Qt::Vertical)
15436 double zeroPointX = valueAxis->coordToPixel(0);
15437 double key;
15438 for (int i=0; i<lineData.size(); ++i)
15440 key = keyAxis->coordToPixel(lineData.at(i).key);
15441 (*linePixelData)[i*2+0].setX(zeroPointX);
15442 (*linePixelData)[i*2+0].setY(key);
15443 (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15444 (*linePixelData)[i*2+1].setY(key);
15446 } else // key axis is horizontal
15448 double zeroPointY = valueAxis->coordToPixel(0);
15449 double key;
15450 for (int i=0; i<lineData.size(); ++i)
15452 key = keyAxis->coordToPixel(lineData.at(i).key);
15453 (*linePixelData)[i*2+0].setX(key);
15454 (*linePixelData)[i*2+0].setY(zeroPointY);
15455 (*linePixelData)[i*2+1].setX(key);
15456 (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15461 /*! \internal
15463 Draws the fill of the graph with the specified brush.
15465 If the fill is a normal fill towards the zero-value-line, only the \a lineData is required (and
15466 two extra points at the zero-value-line, which are added by \ref addFillBasePoints and removed by
15467 \ref removeFillBasePoints after the fill drawing is done).
15469 If the fill is a channel fill between this QCPGraph and another QCPGraph (mChannelFillGraph), the
15470 more complex polygon is calculated with the \ref getChannelFillPolygon function.
15472 \see drawLinePlot
15474 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15476 if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15477 if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15479 applyFillAntialiasingHint(painter);
15480 if (!mChannelFillGraph)
15482 // draw base fill under graph, fill goes all the way to the zero-value-line:
15483 addFillBasePoints(lineData);
15484 painter->setPen(Qt::NoPen);
15485 painter->setBrush(mainBrush());
15486 painter->drawPolygon(QPolygonF(*lineData));
15487 removeFillBasePoints(lineData);
15488 } else
15490 // draw channel fill between this graph and mChannelFillGraph:
15491 painter->setPen(Qt::NoPen);
15492 painter->setBrush(mainBrush());
15493 painter->drawPolygon(getChannelFillPolygon(lineData));
15497 /*! \internal
15499 Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
15500 of the line style and are always drawn if the scatter style's shape is not \ref
15501 QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
15502 functions, together with the (line style dependent) line data.
15504 \see drawLinePlot, drawImpulsePlot
15506 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15508 QCPAxis *keyAxis = mKeyAxis.data();
15509 QCPAxis *valueAxis = mValueAxis.data();
15510 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15512 // draw error bars:
15513 if (mErrorType != etNone)
15515 applyErrorBarsAntialiasingHint(painter);
15516 painter->setPen(mErrorPen);
15517 if (keyAxis->orientation() == Qt::Vertical)
15519 for (int i=0; i<scatterData->size(); ++i)
15520 drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15521 } else
15523 for (int i=0; i<scatterData->size(); ++i)
15524 drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15528 // draw scatter point symbols:
15529 applyScattersAntialiasingHint(painter);
15530 mScatterStyle.applyTo(painter, mPen);
15531 if (keyAxis->orientation() == Qt::Vertical)
15533 for (int i=0; i<scatterData->size(); ++i)
15534 if (!qIsNaN(scatterData->at(i).value))
15535 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15536 } else
15538 for (int i=0; i<scatterData->size(); ++i)
15539 if (!qIsNaN(scatterData->at(i).value))
15540 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15544 /*! \internal
15546 Draws line graphs from the provided data. It connects all points in \a lineData, which was
15547 created by one of the "get(...)PlotData" functions for line styles that require simple line
15548 connections between the point vector they create. These are for example \ref getLinePlotData,
15549 \ref getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
15551 \see drawScatterPlot, drawImpulsePlot
15553 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15555 // draw line of graph:
15556 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15558 applyDefaultAntialiasingHint(painter);
15559 painter->setPen(mainPen());
15560 painter->setBrush(Qt::NoBrush);
15562 /* Draws polyline in batches, currently not used:
15563 int p = 0;
15564 while (p < lineData->size())
15566 int batch = qMin(25, lineData->size()-p);
15567 if (p != 0)
15569 ++batch;
15570 --p; // to draw the connection lines between two batches
15572 painter->drawPolyline(lineData->constData()+p, batch);
15573 p += batch;
15577 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15578 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15579 painter->pen().style() == Qt::SolidLine &&
15580 !painter->modes().testFlag(QCPPainter::pmVectorized) &&
15581 !painter->modes().testFlag(QCPPainter::pmNoCaching))
15583 int i = 0;
15584 bool lastIsNan = false;
15585 const int lineDataSize = lineData->size();
15586 while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
15587 ++i;
15588 ++i; // because drawing works in 1 point retrospect
15589 while (i < lineDataSize)
15591 if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15593 if (!lastIsNan)
15594 painter->drawLine(lineData->at(i-1), lineData->at(i));
15595 else
15596 lastIsNan = false;
15597 } else
15598 lastIsNan = true;
15599 ++i;
15601 } else
15603 int segmentStart = 0;
15604 int i = 0;
15605 const int lineDataSize = lineData->size();
15606 while (i < lineDataSize)
15608 if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15610 painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
15611 segmentStart = i+1;
15613 ++i;
15615 // draw last segment:
15616 painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
15621 /*! \internal
15623 Draws impulses from the provided data, i.e. it connects all line pairs in \a lineData, which was
15624 created by \ref getImpulsePlotData.
15626 \see drawScatterPlot, drawLinePlot
15628 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15630 // draw impulses:
15631 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15633 applyDefaultAntialiasingHint(painter);
15634 QPen pen = mainPen();
15635 pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15636 painter->setPen(pen);
15637 painter->setBrush(Qt::NoBrush);
15638 painter->drawLines(*lineData);
15642 /*! \internal
15644 Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into
15645 consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point
15646 densities.
15648 0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't
15649 needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a
15650 scatterData should be 0 to prevent unnecessary calculations.
15652 This method is used by the various "get(...)PlotData" methods to get the basic working set of data.
15654 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15656 QCPAxis *keyAxis = mKeyAxis.data();
15657 QCPAxis *valueAxis = mValueAxis.data();
15658 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15659 // get visible data range:
15660 QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15661 getVisibleDataBounds(lower, upper);
15662 if (lower == mData->constEnd() || upper == mData->constEnd())
15663 return;
15665 // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15666 int maxCount = std::numeric_limits<int>::max();
15667 if (mAdaptiveSampling)
15669 int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15670 maxCount = 2*keyPixelSpan+2;
15672 int dataCount = countDataInBounds(lower, upper, maxCount);
15674 if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15676 if (lineData)
15678 QCPDataMap::const_iterator it = lower;
15679 QCPDataMap::const_iterator upperEnd = upper+1;
15680 double minValue = it.value().value;
15681 double maxValue = it.value().value;
15682 QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15683 int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15684 int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15685 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15686 double lastIntervalEndKey = currentIntervalStartKey;
15687 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15688 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15689 int intervalDataCount = 1;
15690 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15691 while (it != upperEnd)
15693 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15695 if (it.value().value < minValue)
15696 minValue = it.value().value;
15697 else if (it.value().value > maxValue)
15698 maxValue = it.value().value;
15699 ++intervalDataCount;
15700 } else // new pixel interval started
15702 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15704 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15705 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15706 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15707 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15708 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
15709 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15710 } else
15711 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15712 lastIntervalEndKey = (it-1).value().key;
15713 minValue = it.value().value;
15714 maxValue = it.value().value;
15715 currentIntervalFirstPoint = it;
15716 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15717 if (keyEpsilonVariable)
15718 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15719 intervalDataCount = 1;
15721 ++it;
15723 // handle last interval:
15724 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15726 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15727 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15728 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15729 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15730 } else
15731 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15734 if (scatterData)
15736 double valueMaxRange = valueAxis->range().upper;
15737 double valueMinRange = valueAxis->range().lower;
15738 QCPDataMap::const_iterator it = lower;
15739 QCPDataMap::const_iterator upperEnd = upper+1;
15740 double minValue = it.value().value;
15741 double maxValue = it.value().value;
15742 QCPDataMap::const_iterator minValueIt = it;
15743 QCPDataMap::const_iterator maxValueIt = it;
15744 QCPDataMap::const_iterator currentIntervalStart = it;
15745 int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15746 int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15747 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15748 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15749 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15750 int intervalDataCount = 1;
15751 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15752 while (it != upperEnd)
15754 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15756 if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15758 minValue = it.value().value;
15759 minValueIt = it;
15760 } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15762 maxValue = it.value().value;
15763 maxValueIt = it;
15765 ++intervalDataCount;
15766 } else // new pixel started
15768 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15770 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15771 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15772 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15773 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15774 int c = 0;
15775 while (intervalIt != it)
15777 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15778 scatterData->append(intervalIt.value());
15779 ++c;
15780 ++intervalIt;
15782 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15783 scatterData->append(currentIntervalStart.value());
15784 minValue = it.value().value;
15785 maxValue = it.value().value;
15786 currentIntervalStart = it;
15787 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15788 if (keyEpsilonVariable)
15789 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15790 intervalDataCount = 1;
15792 ++it;
15794 // handle last interval:
15795 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15797 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15798 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15799 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15800 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15801 int c = 0;
15802 while (intervalIt != it)
15804 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15805 scatterData->append(intervalIt.value());
15806 ++c;
15807 ++intervalIt;
15809 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15810 scatterData->append(currentIntervalStart.value());
15812 } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15814 QVector<QCPData> *dataVector = 0;
15815 if (lineData)
15816 dataVector = lineData;
15817 else if (scatterData)
15818 dataVector = scatterData;
15819 if (dataVector)
15821 QCPDataMap::const_iterator it = lower;
15822 QCPDataMap::const_iterator upperEnd = upper+1;
15823 dataVector->reserve(dataCount+2); // +2 for possible fill end points
15824 while (it != upperEnd)
15826 dataVector->append(it.value());
15827 ++it;
15830 if (lineData && scatterData)
15831 *scatterData = *dataVector;
15835 /*! \internal
15837 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
15838 point. \a x and \a y pixel positions of the data point are passed since they are already known in
15839 pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
15840 data is therefore only used for the errors, not key and value.
15842 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15844 if (qIsNaN(data.value))
15845 return;
15846 QCPAxis *keyAxis = mKeyAxis.data();
15847 QCPAxis *valueAxis = mValueAxis.data();
15848 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15850 double a, b; // positions of error bar bounds in pixels
15851 double barWidthHalf = mErrorBarSize*0.5;
15852 double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15854 if (keyAxis->orientation() == Qt::Vertical)
15856 // draw key error vertically and value error horizontally
15857 if (mErrorType == etKey || mErrorType == etBoth)
15859 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15860 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15861 if (keyAxis->rangeReversed())
15862 qSwap(a,b);
15863 // draw spine:
15864 if (mErrorBarSkipSymbol)
15866 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15867 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15868 if (y-b > skipSymbolMargin)
15869 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15870 } else
15871 painter->drawLine(QLineF(x, a, x, b));
15872 // draw handles:
15873 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15874 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15876 if (mErrorType == etValue || mErrorType == etBoth)
15878 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15879 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15880 if (valueAxis->rangeReversed())
15881 qSwap(a,b);
15882 // draw spine:
15883 if (mErrorBarSkipSymbol)
15885 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15886 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15887 if (b-x > skipSymbolMargin)
15888 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15889 } else
15890 painter->drawLine(QLineF(a, y, b, y));
15891 // draw handles:
15892 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15893 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15895 } else // mKeyAxis->orientation() is Qt::Horizontal
15897 // draw value error vertically and key error horizontally
15898 if (mErrorType == etKey || mErrorType == etBoth)
15900 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15901 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15902 if (keyAxis->rangeReversed())
15903 qSwap(a,b);
15904 // draw spine:
15905 if (mErrorBarSkipSymbol)
15907 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15908 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15909 if (b-x > skipSymbolMargin)
15910 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15911 } else
15912 painter->drawLine(QLineF(a, y, b, y));
15913 // draw handles:
15914 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15915 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15917 if (mErrorType == etValue || mErrorType == etBoth)
15919 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15920 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15921 if (valueAxis->rangeReversed())
15922 qSwap(a,b);
15923 // draw spine:
15924 if (mErrorBarSkipSymbol)
15926 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15927 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15928 if (y-b > skipSymbolMargin)
15929 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15930 } else
15931 painter->drawLine(QLineF(x, a, x, b));
15932 // draw handles:
15933 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15934 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15939 /*! \internal
15941 called by \ref getPreparedData to determine which data (key) range is visible at the current key
15942 axis range setting, so only that needs to be processed.
15944 \a lower returns an iterator to the lowest data point that needs to be taken into account when
15945 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
15946 lower may still be just outside the visible range.
15948 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
15949 just outside of the visible range.
15951 if the graph contains no data, both \a lower and \a upper point to constEnd.
15953 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15955 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15956 if (mData->isEmpty())
15958 lower = mData->constEnd();
15959 upper = mData->constEnd();
15960 return;
15963 // get visible data range as QMap iterators
15964 QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
15965 QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
15966 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15967 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15969 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15970 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15973 /*! \internal
15975 Counts the number of data points between \a lower and \a upper (including them), up to a maximum
15976 of \a maxCount.
15978 This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
15979 used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
15980 only needs to be done until \a maxCount is reached, which should be set to the number of data
15981 points at which adaptive sampling sets in.
15983 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15985 if (upper == mData->constEnd() && lower == mData->constEnd())
15986 return 0;
15987 QCPDataMap::const_iterator it = lower;
15988 int count = 1;
15989 while (it != upper && count < maxCount)
15991 ++it;
15992 ++count;
15994 return count;
15997 /*! \internal
15999 The line data vector generated by e.g. getLinePlotData contains only the line that connects the
16000 data points. If the graph needs to be filled, two additional points need to be added at the
16001 value-zero-line in the lower and upper key positions of the graph. This function calculates these
16002 points and adds them to the end of \a lineData. Since the fill is typically drawn before the line
16003 stroke, these added points need to be removed again after the fill is done, with the
16004 removeFillBasePoints function.
16006 The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
16007 because the data vector generation functions (getLinePlotData etc.) reserve two extra points when
16008 they allocate memory for \a lineData.
16010 \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16012 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16014 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16016 // append points that close the polygon fill at the key axis:
16017 if (mKeyAxis.data()->orientation() == Qt::Vertical)
16019 *lineData << upperFillBasePoint(lineData->last().y());
16020 *lineData << lowerFillBasePoint(lineData->first().y());
16021 } else
16023 *lineData << upperFillBasePoint(lineData->last().x());
16024 *lineData << lowerFillBasePoint(lineData->first().x());
16028 /*! \internal
16030 removes the two points from \a lineData that were added by \ref addFillBasePoints.
16032 \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16034 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16036 lineData->remove(lineData->size()-2, 2);
16039 /*! \internal
16041 called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon
16042 on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale
16043 case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative
16044 infinity. So this case is handled separately by just closing the fill polygon on the axis which
16045 lies in the direction towards the zero value.
16047 \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key
16048 axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned
16049 point, respectively.
16051 \see upperFillBasePoint, addFillBasePoints
16053 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16055 QCPAxis *keyAxis = mKeyAxis.data();
16056 QCPAxis *valueAxis = mValueAxis.data();
16057 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16059 QPointF point;
16060 if (valueAxis->scaleType() == QCPAxis::stLinear)
16062 if (keyAxis->axisType() == QCPAxis::atLeft)
16064 point.setX(valueAxis->coordToPixel(0));
16065 point.setY(lowerKey);
16066 } else if (keyAxis->axisType() == QCPAxis::atRight)
16068 point.setX(valueAxis->coordToPixel(0));
16069 point.setY(lowerKey);
16070 } else if (keyAxis->axisType() == QCPAxis::atTop)
16072 point.setX(lowerKey);
16073 point.setY(valueAxis->coordToPixel(0));
16074 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16076 point.setX(lowerKey);
16077 point.setY(valueAxis->coordToPixel(0));
16079 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16081 // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16082 // to the axis which is in the direction towards zero
16083 if (keyAxis->orientation() == Qt::Vertical)
16085 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16086 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16087 point.setX(keyAxis->axisRect()->right());
16088 else
16089 point.setX(keyAxis->axisRect()->left());
16090 point.setY(lowerKey);
16091 } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16093 point.setX(lowerKey);
16094 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16095 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16096 point.setY(keyAxis->axisRect()->top());
16097 else
16098 point.setY(keyAxis->axisRect()->bottom());
16101 return point;
16104 /*! \internal
16106 called by \ref addFillBasePoints to conveniently assign the point which closes the fill
16107 polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
16108 scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
16109 negative infinity. So this case is handled separately by just closing the fill polygon on the
16110 axis which lies in the direction towards the zero value.
16112 \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key
16113 axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned
16114 point, respectively.
16116 \see lowerFillBasePoint, addFillBasePoints
16118 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16120 QCPAxis *keyAxis = mKeyAxis.data();
16121 QCPAxis *valueAxis = mValueAxis.data();
16122 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16124 QPointF point;
16125 if (valueAxis->scaleType() == QCPAxis::stLinear)
16127 if (keyAxis->axisType() == QCPAxis::atLeft)
16129 point.setX(valueAxis->coordToPixel(0));
16130 point.setY(upperKey);
16131 } else if (keyAxis->axisType() == QCPAxis::atRight)
16133 point.setX(valueAxis->coordToPixel(0));
16134 point.setY(upperKey);
16135 } else if (keyAxis->axisType() == QCPAxis::atTop)
16137 point.setX(upperKey);
16138 point.setY(valueAxis->coordToPixel(0));
16139 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16141 point.setX(upperKey);
16142 point.setY(valueAxis->coordToPixel(0));
16144 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16146 // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16147 // to the axis which is in the direction towards 0
16148 if (keyAxis->orientation() == Qt::Vertical)
16150 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16151 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16152 point.setX(keyAxis->axisRect()->right());
16153 else
16154 point.setX(keyAxis->axisRect()->left());
16155 point.setY(upperKey);
16156 } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16158 point.setX(upperKey);
16159 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16160 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16161 point.setY(keyAxis->axisRect()->top());
16162 else
16163 point.setY(keyAxis->axisRect()->bottom());
16166 return point;
16169 /*! \internal
16171 Generates the polygon needed for drawing channel fills between this graph (data passed via \a
16172 lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
16173 getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
16174 target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
16175 key axes vertical). For increased performance (due to implicit sharing), keep the returned
16176 QPolygonF const.
16178 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16180 if (!mChannelFillGraph)
16181 return QPolygonF();
16183 QCPAxis *keyAxis = mKeyAxis.data();
16184 QCPAxis *valueAxis = mValueAxis.data();
16185 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16186 if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16188 if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16189 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)
16191 if (lineData->isEmpty()) return QPolygonF();
16192 QVector<QPointF> otherData;
16193 mChannelFillGraph.data()->getPlotData(&otherData, 0);
16194 if (otherData.isEmpty()) return QPolygonF();
16195 QVector<QPointF> thisData;
16196 thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16197 for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16198 thisData << lineData->at(i);
16200 // pointers to be able to swap them, depending which data range needs cropping:
16201 QVector<QPointF> *staticData = &thisData;
16202 QVector<QPointF> *croppedData = &otherData;
16204 // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16205 if (keyAxis->orientation() == Qt::Horizontal)
16207 // x is key
16208 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16209 if (staticData->first().x() > staticData->last().x())
16211 int size = staticData->size();
16212 for (int i=0; i<size/2; ++i)
16213 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16215 if (croppedData->first().x() > croppedData->last().x())
16217 int size = croppedData->size();
16218 for (int i=0; i<size/2; ++i)
16219 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16221 // crop lower bound:
16222 if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16223 qSwap(staticData, croppedData);
16224 int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16225 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16226 croppedData->remove(0, lowBound);
16227 // set lowest point of cropped data to fit exactly key position of first static data
16228 // point via linear interpolation:
16229 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16230 double slope;
16231 if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16232 slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16233 else
16234 slope = 0;
16235 (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16236 (*croppedData)[0].setX(staticData->first().x());
16238 // crop upper bound:
16239 if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16240 qSwap(staticData, croppedData);
16241 int highBound = findIndexAboveX(croppedData, staticData->last().x());
16242 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16243 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16244 // set highest point of cropped data to fit exactly key position of last static data
16245 // point via linear interpolation:
16246 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16247 int li = croppedData->size()-1; // last index
16248 if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16249 slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16250 else
16251 slope = 0;
16252 (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16253 (*croppedData)[li].setX(staticData->last().x());
16254 } else // mKeyAxis->orientation() == Qt::Vertical
16256 // y is key
16257 // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16258 // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16259 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16260 if (staticData->first().y() < staticData->last().y())
16262 int size = staticData->size();
16263 for (int i=0; i<size/2; ++i)
16264 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16266 if (croppedData->first().y() < croppedData->last().y())
16268 int size = croppedData->size();
16269 for (int i=0; i<size/2; ++i)
16270 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16272 // crop lower bound:
16273 if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16274 qSwap(staticData, croppedData);
16275 int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16276 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16277 croppedData->remove(0, lowBound);
16278 // set lowest point of cropped data to fit exactly key position of first static data
16279 // point via linear interpolation:
16280 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16281 double slope;
16282 if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16283 slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16284 else
16285 slope = 0;
16286 (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16287 (*croppedData)[0].setY(staticData->first().y());
16289 // crop upper bound:
16290 if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16291 qSwap(staticData, croppedData);
16292 int highBound = findIndexBelowY(croppedData, staticData->last().y());
16293 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16294 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16295 // set highest point of cropped data to fit exactly key position of last static data
16296 // point via linear interpolation:
16297 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16298 int li = croppedData->size()-1; // last index
16299 if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16300 slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16301 else
16302 slope = 0;
16303 (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16304 (*croppedData)[li].setY(staticData->last().y());
16307 // return joined:
16308 for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16309 thisData << otherData.at(i);
16310 return QPolygonF(thisData);
16313 /*! \internal
16315 Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in
16316 \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16318 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16320 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16322 for (int i=data->size()-1; i>=0; --i)
16324 if (data->at(i).x() < x)
16326 if (i<data->size()-1)
16327 return i+1;
16328 else
16329 return data->size()-1;
16332 return -1;
16335 /*! \internal
16337 Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in
16338 \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16340 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16342 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16344 for (int i=0; i<data->size(); ++i)
16346 if (data->at(i).x() > x)
16348 if (i>0)
16349 return i-1;
16350 else
16351 return 0;
16354 return -1;
16357 /*! \internal
16359 Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in
16360 \a data points are ordered descending, as is the case when plotting with vertical key axis.
16362 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16364 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16366 for (int i=0; i<data->size(); ++i)
16368 if (data->at(i).y() < y)
16370 if (i>0)
16371 return i-1;
16372 else
16373 return 0;
16376 return -1;
16379 /*! \internal
16381 Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
16382 pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
16383 \ref selectTest.
16385 If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape
16386 is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0.
16388 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16390 if (mData->isEmpty())
16391 return -1.0;
16392 if (mLineStyle == lsNone && mScatterStyle.isNone())
16393 return -1.0;
16395 // calculate minimum distances to graph representation:
16396 if (mLineStyle == lsNone)
16398 // no line displayed, only calculate distance to scatter points:
16399 QVector<QCPData> scatterData;
16400 getScatterPlotData(&scatterData);
16401 if (scatterData.size() > 0)
16403 double minDistSqr = std::numeric_limits<double>::max();
16404 for (int i=0; i<scatterData.size(); ++i)
16406 double currentDistSqr = QVector2D(coordsToPixels(scatterData.at(i).key, scatterData.at(i).value)-pixelPoint).lengthSquared();
16407 if (currentDistSqr < minDistSqr)
16408 minDistSqr = currentDistSqr;
16410 return qSqrt(minDistSqr);
16411 } else // no data available in view to calculate distance to
16412 return -1.0;
16413 } else
16415 // line displayed, calculate distance to line segments:
16416 QVector<QPointF> lineData;
16417 getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16418 if (lineData.size() > 1) // at least one line segment, compare distance to line segments
16420 double minDistSqr = std::numeric_limits<double>::max();
16421 if (mLineStyle == lsImpulse)
16423 // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16424 for (int i=0; i<lineData.size()-1; i+=2) // iterate pairs
16426 double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16427 if (currentDistSqr < minDistSqr)
16428 minDistSqr = currentDistSqr;
16430 } else
16432 // all other line plots (line and step) connect points directly:
16433 for (int i=0; i<lineData.size()-1; ++i)
16435 double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
16436 if (currentDistSqr < minDistSqr)
16437 minDistSqr = currentDistSqr;
16440 return qSqrt(minDistSqr);
16441 } else if (lineData.size() > 0) // only single data point, calculate distance to that point
16443 return QVector2D(lineData.at(0)-pixelPoint).length();
16444 } else // no data available in view to calculate distance to
16445 return -1.0;
16449 /*! \internal
16451 Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in
16452 \a data points are ordered descending, as is the case when plotting with vertical key axis (since
16453 keys are ordered ascending).
16455 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16457 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16459 for (int i=data->size()-1; i>=0; --i)
16461 if (data->at(i).y() > y)
16463 if (i<data->size()-1)
16464 return i+1;
16465 else
16466 return data->size()-1;
16469 return -1;
16472 /* inherits documentation from base class */
16473 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16475 // just call the specialized version which takes an additional argument whether error bars
16476 // should also be taken into consideration for range calculation. We set this to true here.
16477 return getKeyRange(foundRange, inSignDomain, true);
16480 /* inherits documentation from base class */
16481 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16483 // just call the specialized version which takes an additional argument whether error bars
16484 // should also be taken into consideration for range calculation. We set this to true here.
16485 return getValueRange(foundRange, inSignDomain, true);
16488 /*! \overload
16490 Allows to specify whether the error bars should be included in the range calculation.
16492 \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
16494 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16496 QCPRange range;
16497 bool haveLower = false;
16498 bool haveUpper = false;
16500 double current, currentErrorMinus, currentErrorPlus;
16502 if (inSignDomain == sdBoth) // range may be anywhere
16504 QCPDataMap::const_iterator it = mData->constBegin();
16505 while (it != mData->constEnd())
16507 if (!qIsNaN(it.value().value))
16509 current = it.value().key;
16510 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16511 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16512 if (current-currentErrorMinus < range.lower || !haveLower)
16514 range.lower = current-currentErrorMinus;
16515 haveLower = true;
16517 if (current+currentErrorPlus > range.upper || !haveUpper)
16519 range.upper = current+currentErrorPlus;
16520 haveUpper = true;
16523 ++it;
16525 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16527 QCPDataMap::const_iterator it = mData->constBegin();
16528 while (it != mData->constEnd())
16530 if (!qIsNaN(it.value().value))
16532 current = it.value().key;
16533 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16534 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16535 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16537 range.lower = current-currentErrorMinus;
16538 haveLower = true;
16540 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16542 range.upper = current+currentErrorPlus;
16543 haveUpper = true;
16545 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16547 if ((current < range.lower || !haveLower) && current < 0)
16549 range.lower = current;
16550 haveLower = true;
16552 if ((current > range.upper || !haveUpper) && current < 0)
16554 range.upper = current;
16555 haveUpper = true;
16559 ++it;
16561 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16563 QCPDataMap::const_iterator it = mData->constBegin();
16564 while (it != mData->constEnd())
16566 if (!qIsNaN(it.value().value))
16568 current = it.value().key;
16569 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16570 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16571 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16573 range.lower = current-currentErrorMinus;
16574 haveLower = true;
16576 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16578 range.upper = current+currentErrorPlus;
16579 haveUpper = true;
16581 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16583 if ((current < range.lower || !haveLower) && current > 0)
16585 range.lower = current;
16586 haveLower = true;
16588 if ((current > range.upper || !haveUpper) && current > 0)
16590 range.upper = current;
16591 haveUpper = true;
16595 ++it;
16599 foundRange = haveLower && haveUpper;
16600 return range;
16603 /*! \overload
16605 Allows to specify whether the error bars should be included in the range calculation.
16607 \see getValueRange(bool &foundRange, SignDomain inSignDomain)
16609 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16611 QCPRange range;
16612 bool haveLower = false;
16613 bool haveUpper = false;
16615 double current, currentErrorMinus, currentErrorPlus;
16617 if (inSignDomain == sdBoth) // range may be anywhere
16619 QCPDataMap::const_iterator it = mData->constBegin();
16620 while (it != mData->constEnd())
16622 current = it.value().value;
16623 if (!qIsNaN(current))
16625 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16626 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16627 if (current-currentErrorMinus < range.lower || !haveLower)
16629 range.lower = current-currentErrorMinus;
16630 haveLower = true;
16632 if (current+currentErrorPlus > range.upper || !haveUpper)
16634 range.upper = current+currentErrorPlus;
16635 haveUpper = true;
16638 ++it;
16640 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16642 QCPDataMap::const_iterator it = mData->constBegin();
16643 while (it != mData->constEnd())
16645 current = it.value().value;
16646 if (!qIsNaN(current))
16648 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16649 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16650 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16652 range.lower = current-currentErrorMinus;
16653 haveLower = true;
16655 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16657 range.upper = current+currentErrorPlus;
16658 haveUpper = true;
16660 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16662 if ((current < range.lower || !haveLower) && current < 0)
16664 range.lower = current;
16665 haveLower = true;
16667 if ((current > range.upper || !haveUpper) && current < 0)
16669 range.upper = current;
16670 haveUpper = true;
16674 ++it;
16676 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16678 QCPDataMap::const_iterator it = mData->constBegin();
16679 while (it != mData->constEnd())
16681 current = it.value().value;
16682 if (!qIsNaN(current))
16684 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16685 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16686 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16688 range.lower = current-currentErrorMinus;
16689 haveLower = true;
16691 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16693 range.upper = current+currentErrorPlus;
16694 haveUpper = true;
16696 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16698 if ((current < range.lower || !haveLower) && current > 0)
16700 range.lower = current;
16701 haveLower = true;
16703 if ((current > range.upper || !haveUpper) && current > 0)
16705 range.upper = current;
16706 haveUpper = true;
16710 ++it;
16714 foundRange = haveLower && haveUpper;
16715 return range;
16719 ////////////////////////////////////////////////////////////////////////////////////////////////////
16720 //////////////////// QCPCurveData
16721 ////////////////////////////////////////////////////////////////////////////////////////////////////
16723 /*! \class QCPCurveData
16724 \brief Holds the data of one single data point for QCPCurve.
16726 The container for storing multiple data points is \ref QCPCurveDataMap.
16728 The stored data is:
16729 \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
16730 \li \a key: coordinate on the key axis of this curve point
16731 \li \a value: coordinate on the value axis of this curve point
16733 \see QCPCurveDataMap
16737 Constructs a curve data point with t, key and value set to zero.
16739 QCPCurveData::QCPCurveData() :
16740 t(0),
16741 key(0),
16742 value(0)
16747 Constructs a curve data point with the specified \a t, \a key and \a value.
16749 QCPCurveData::QCPCurveData(double t, double key, double value) :
16750 t(t),
16751 key(key),
16752 value(value)
16757 ////////////////////////////////////////////////////////////////////////////////////////////////////
16758 //////////////////// QCPCurve
16759 ////////////////////////////////////////////////////////////////////////////////////////////////////
16761 /*! \class QCPCurve
16762 \brief A plottable representing a parametric curve in a plot.
16764 \image html QCPCurve.png
16766 Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate,
16767 so their visual representation can have \a loops. This is realized by introducing a third
16768 coordinate \a t, which defines the order of the points described by the other two coordinates \a
16769 x and \a y.
16771 To plot data, assign it with the \ref setData or \ref addData functions.
16773 Gaps in the curve can be created by adding data points with NaN as key and value
16774 (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
16775 separated.
16777 \section appearance Changing the appearance
16779 The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
16780 \section usage Usage
16782 Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
16783 the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
16785 Usually, you first create an instance and add it to the customPlot:
16786 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1
16787 and then modify the properties of the newly created plottable, e.g.:
16788 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2
16792 Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
16793 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
16794 the same orientation. If either of these restrictions is violated, a corresponding message is
16795 printed to the debug output (qDebug), the construction is not aborted, though.
16797 The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
16798 then takes ownership of the graph.
16800 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16801 QCPAbstractPlottable(keyAxis, valueAxis)
16803 mData = new QCPCurveDataMap;
16804 mPen.setColor(Qt::blue);
16805 mPen.setStyle(Qt::SolidLine);
16806 mBrush.setColor(Qt::blue);
16807 mBrush.setStyle(Qt::NoBrush);
16808 mSelectedPen = mPen;
16809 mSelectedPen.setWidthF(2.5);
16810 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16811 mSelectedBrush = mBrush;
16813 setScatterStyle(QCPScatterStyle());
16814 setLineStyle(lsLine);
16817 QCPCurve::~QCPCurve()
16819 delete mData;
16823 Replaces the current data with the provided \a data.
16825 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
16826 takes ownership of the passed data and replaces the internal data pointer with it. This is
16827 significantly faster than copying for large datasets.
16829 void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
16831 if (mData == data)
16833 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
16834 return;
16836 if (copy)
16838 *mData = *data;
16839 } else
16841 delete mData;
16842 mData = data;
16846 /*! \overload
16848 Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
16849 provided vectors should have equal length. Else, the number of added points will be the size of
16850 the smallest vector.
16852 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16854 mData->clear();
16855 int n = t.size();
16856 n = qMin(n, key.size());
16857 n = qMin(n, value.size());
16858 QCPCurveData newData;
16859 for (int i=0; i<n; ++i)
16861 newData.t = t[i];
16862 newData.key = key[i];
16863 newData.value = value[i];
16864 mData->insertMulti(newData.t, newData);
16868 /*! \overload
16870 Replaces the current data with the provided \a key and \a value pairs. The t parameter
16871 of each data point will be set to the integer index of the respective key/value pair.
16873 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16875 mData->clear();
16876 int n = key.size();
16877 n = qMin(n, value.size());
16878 QCPCurveData newData;
16879 for (int i=0; i<n; ++i)
16881 newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16882 newData.key = key[i];
16883 newData.value = value[i];
16884 mData->insertMulti(newData.t, newData);
16889 Sets the visual appearance of single data points in the plot. If set to \ref
16890 QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate
16891 line style).
16893 \see QCPScatterStyle, setLineStyle
16895 void QCPCurve::setScatterStyle(const QCPScatterStyle &style)
16897 mScatterStyle = style;
16901 Sets how the single data points are connected in the plot or how they are represented visually
16902 apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
16903 setScatterStyle to the desired scatter style.
16905 \see setScatterStyle
16907 void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
16909 mLineStyle = style;
16913 Adds the provided data points in \a dataMap to the current data.
16914 \see removeData
16916 void QCPCurve::addData(const QCPCurveDataMap &dataMap)
16918 mData->unite(dataMap);
16921 /*! \overload
16922 Adds the provided single data point in \a data to the current data.
16923 \see removeData
16925 void QCPCurve::addData(const QCPCurveData &data)
16927 mData->insertMulti(data.t, data);
16930 /*! \overload
16931 Adds the provided single data point as \a t, \a key and \a value tuple to the current data
16932 \see removeData
16934 void QCPCurve::addData(double t, double key, double value)
16936 QCPCurveData newData;
16937 newData.t = t;
16938 newData.key = key;
16939 newData.value = value;
16940 mData->insertMulti(newData.t, newData);
16943 /*! \overload
16945 Adds the provided single data point as \a key and \a value pair to the current data The t
16946 parameter of the data point is set to the t of the last data point plus 1. If there is no last
16947 data point, t will be set to 0.
16949 \see removeData
16951 void QCPCurve::addData(double key, double value)
16953 QCPCurveData newData;
16954 if (!mData->isEmpty())
16955 newData.t = (mData->constEnd()-1).key()+1;
16956 else
16957 newData.t = 0;
16958 newData.key = key;
16959 newData.value = value;
16960 mData->insertMulti(newData.t, newData);
16963 /*! \overload
16964 Adds the provided data points as \a t, \a key and \a value tuples to the current data.
16965 \see removeData
16967 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16969 int n = ts.size();
16970 n = qMin(n, keys.size());
16971 n = qMin(n, values.size());
16972 QCPCurveData newData;
16973 for (int i=0; i<n; ++i)
16975 newData.t = ts[i];
16976 newData.key = keys[i];
16977 newData.value = values[i];
16978 mData->insertMulti(newData.t, newData);
16983 Removes all data points with curve parameter t smaller than \a t.
16984 \see addData, clearData
16986 void QCPCurve::removeDataBefore(double t)
16988 QCPCurveDataMap::iterator it = mData->begin();
16989 while (it != mData->end() && it.key() < t)
16990 it = mData->erase(it);
16994 Removes all data points with curve parameter t greater than \a t.
16995 \see addData, clearData
16997 void QCPCurve::removeDataAfter(double t)
16999 if (mData->isEmpty()) return;
17000 QCPCurveDataMap::iterator it = mData->upperBound(t);
17001 while (it != mData->end())
17002 it = mData->erase(it);
17006 Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
17007 greater or equal to \a tot, the function does nothing. To remove a single data point with known
17008 t, use \ref removeData(double t).
17010 \see addData, clearData
17012 void QCPCurve::removeData(double fromt, double tot)
17014 if (fromt >= tot || mData->isEmpty()) return;
17015 QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17016 QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17017 while (it != itEnd)
17018 it = mData->erase(it);
17021 /*! \overload
17023 Removes a single data point at curve parameter \a t. If the position is not known with absolute
17024 precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
17025 interval around the suspected position, depeding on the precision with which the curve parameter
17026 is known.
17028 \see addData, clearData
17030 void QCPCurve::removeData(double t)
17032 mData->remove(t);
17036 Removes all data points.
17037 \see removeData, removeDataAfter, removeDataBefore
17039 void QCPCurve::clearData()
17041 mData->clear();
17044 /* inherits documentation from base class */
17045 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17047 Q_UNUSED(details)
17048 if ((onlySelectable && !mSelectable) || mData->isEmpty())
17049 return -1;
17050 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17052 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17053 return pointDistance(pos);
17054 else
17055 return -1;
17058 /* inherits documentation from base class */
17059 void QCPCurve::draw(QCPPainter *painter)
17061 if (mData->isEmpty()) return;
17063 // allocate line vector:
17064 QVector<QPointF> *lineData = new QVector<QPointF>;
17066 // fill with curve data:
17067 getCurveData(lineData);
17069 // check data validity if flag set:
17070 #ifdef QCUSTOMPLOT_CHECK_DATA
17071 QCPCurveDataMap::const_iterator it;
17072 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17074 if (QCP::isInvalidData(it.value().t) ||
17075 QCP::isInvalidData(it.value().key, it.value().value))
17076 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17078 #endif
17080 // draw curve fill:
17081 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17083 applyFillAntialiasingHint(painter);
17084 painter->setPen(Qt::NoPen);
17085 painter->setBrush(mainBrush());
17086 painter->drawPolygon(QPolygonF(*lineData));
17089 // draw curve line:
17090 if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17092 applyDefaultAntialiasingHint(painter);
17093 painter->setPen(mainPen());
17094 painter->setBrush(Qt::NoBrush);
17095 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17096 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17097 painter->pen().style() == Qt::SolidLine &&
17098 !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17099 !painter->modes().testFlag(QCPPainter::pmNoCaching))
17101 int i = 0;
17102 bool lastIsNan = false;
17103 const int lineDataSize = lineData->size();
17104 while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
17105 ++i;
17106 ++i; // because drawing works in 1 point retrospect
17107 while (i < lineDataSize)
17109 if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17111 if (!lastIsNan)
17112 painter->drawLine(lineData->at(i-1), lineData->at(i));
17113 else
17114 lastIsNan = false;
17115 } else
17116 lastIsNan = true;
17117 ++i;
17119 } else
17121 int segmentStart = 0;
17122 int i = 0;
17123 const int lineDataSize = lineData->size();
17124 while (i < lineDataSize)
17126 if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17128 painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17129 segmentStart = i+1;
17131 ++i;
17133 // draw last segment:
17134 painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
17138 // draw scatters:
17139 if (!mScatterStyle.isNone())
17140 drawScatterPlot(painter, lineData);
17142 // free allocated line data:
17143 delete lineData;
17146 /* inherits documentation from base class */
17147 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17149 // draw fill:
17150 if (mBrush.style() != Qt::NoBrush)
17152 applyFillAntialiasingHint(painter);
17153 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17155 // draw line vertically centered:
17156 if (mLineStyle != lsNone)
17158 applyDefaultAntialiasingHint(painter);
17159 painter->setPen(mPen);
17160 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
17162 // draw scatter symbol:
17163 if (!mScatterStyle.isNone())
17165 applyScattersAntialiasingHint(painter);
17166 // scale scatter pixmap if it's too large to fit in legend icon rect:
17167 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17169 QCPScatterStyle scaledStyle(mScatterStyle);
17170 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17171 scaledStyle.applyTo(painter, mPen);
17172 scaledStyle.drawShape(painter, QRectF(rect).center());
17173 } else
17175 mScatterStyle.applyTo(painter, mPen);
17176 mScatterStyle.drawShape(painter, QRectF(rect).center());
17181 /*! \internal
17183 Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
17184 the line style and are always drawn if scatter shape is not \ref QCPScatterStyle::ssNone.
17186 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17188 // draw scatter point symbols:
17189 applyScattersAntialiasingHint(painter);
17190 mScatterStyle.applyTo(painter, mPen);
17191 for (int i=0; i<pointData->size(); ++i)
17192 if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17193 mScatterStyle.drawShape(painter, pointData->at(i));
17196 /*! \internal
17198 called by QCPCurve::draw to generate a point vector (in pixel coordinates) which represents the
17199 line of the curve.
17201 Line segments that aren't visible in the current axis rect are handled in an optimized way. They
17202 are projected onto a rectangle slightly larger than the visible axis rect and simplified
17203 regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside
17204 the visible axis rect by generating new temporary points on the outer rect if necessary.
17206 Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref
17207 getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints.
17209 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17211 QCPAxis *keyAxis = mKeyAxis.data();
17212 QCPAxis *valueAxis = mValueAxis.data();
17213 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17215 // add margins to rect to compensate for stroke width
17216 double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17217 if (!mScatterStyle.isNone())
17218 strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17219 double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17220 double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17221 double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17222 double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17223 int currentRegion;
17224 QCPCurveDataMap::const_iterator it = mData->constBegin();
17225 QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
17226 int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17227 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)
17228 while (it != mData->constEnd())
17230 currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17231 if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17233 if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17235 QPointF crossA, crossB;
17236 if (prevRegion == 5) // we're coming from R, so add this point optimized
17238 lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17239 // 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
17240 *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17241 } else if (mayTraverse(prevRegion, currentRegion) &&
17242 getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17244 // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17245 QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17246 getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17247 if (it != mData->constBegin())
17249 *lineData << beforeTraverseCornerPoints;
17250 lineData->append(crossA);
17251 lineData->append(crossB);
17252 *lineData << afterTraverseCornerPoints;
17253 } else
17255 lineData->append(crossB);
17256 *lineData << afterTraverseCornerPoints;
17257 trailingPoints << beforeTraverseCornerPoints << crossA ;
17259 } 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)
17261 *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17263 } else // segment does end in R, so we add previous point optimized and this point at original position
17265 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
17266 trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17267 else
17268 lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17269 lineData->append(coordsToPixels(it.value().key, it.value().value));
17271 } else // region didn't change
17273 if (currentRegion == 5) // still in R, keep adding original points
17275 lineData->append(coordsToPixels(it.value().key, it.value().value));
17276 } else // still outside R, no need to add anything
17278 // see how this is not doing anything? That's the main optimization...
17281 prevIt = it;
17282 prevRegion = currentRegion;
17283 ++it;
17285 *lineData << trailingPoints;
17288 /*! \internal
17290 This function is part of the curve optimization algorithm of \ref getCurveData.
17292 It returns the region of the given point (\a x, \a y) with respect to a rectangle defined by \a
17293 rectLeft, \a rectTop, \a rectRight, and \a rectBottom.
17295 The regions are enumerated from top to bottom and left to right:
17297 <table style="width:10em; text-align:center">
17298 <tr><td>1</td><td>4</td><td>7</td></tr>
17299 <tr><td>2</td><td style="border:1px solid black">5</td><td>8</td></tr>
17300 <tr><td>3</td><td>6</td><td>9</td></tr>
17301 </table>
17303 With the rectangle being region 5, and the outer regions extending infinitely outwards. In the
17304 curve optimization algorithm, region 5 is considered to be the visible portion of the plot.
17306 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17308 if (x < rectLeft) // region 123
17310 if (y > rectTop)
17311 return 1;
17312 else if (y < rectBottom)
17313 return 3;
17314 else
17315 return 2;
17316 } else if (x > rectRight) // region 789
17318 if (y > rectTop)
17319 return 7;
17320 else if (y < rectBottom)
17321 return 9;
17322 else
17323 return 8;
17324 } else // region 456
17326 if (y > rectTop)
17327 return 4;
17328 else if (y < rectBottom)
17329 return 6;
17330 else
17331 return 5;
17335 /*! \internal
17337 This function is part of the curve optimization algorithm of \ref getCurveData.
17339 This method is used in case the current segment passes from inside the visible rect (region 5,
17340 see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by
17341 the line connecting (\a key, \a value) with (\a otherKey, \a otherValue).
17343 It returns the intersection point of the segment with the border of region 5.
17345 For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or
17346 whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or
17347 leaving it. It is important though that \a otherRegion correctly identifies the other region not
17348 equal to 5.
17350 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17352 double intersectKey = rectLeft; // initial value is just fail-safe
17353 double intersectValue = rectTop; // initial value is just fail-safe
17354 switch (otherRegion)
17356 case 1: // top and left edge
17358 intersectValue = rectTop;
17359 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17360 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17362 intersectKey = rectLeft;
17363 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17365 break;
17367 case 2: // left edge
17369 intersectKey = rectLeft;
17370 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17371 break;
17373 case 3: // bottom and left edge
17375 intersectValue = rectBottom;
17376 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17377 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17379 intersectKey = rectLeft;
17380 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17382 break;
17384 case 4: // top edge
17386 intersectValue = rectTop;
17387 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17388 break;
17390 case 5:
17392 break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17394 case 6: // bottom edge
17396 intersectValue = rectBottom;
17397 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17398 break;
17400 case 7: // top and right edge
17402 intersectValue = rectTop;
17403 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17404 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17406 intersectKey = rectRight;
17407 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17409 break;
17411 case 8: // right edge
17413 intersectKey = rectRight;
17414 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17415 break;
17417 case 9: // bottom and right edge
17419 intersectValue = rectBottom;
17420 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17421 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17423 intersectKey = rectRight;
17424 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17426 break;
17429 return coordsToPixels(intersectKey, intersectValue);
17432 /*! \internal
17434 This function is part of the curve optimization algorithm of \ref getCurveData.
17436 In situations where a single segment skips over multiple regions it might become necessary to add
17437 extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment
17438 doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts.
17439 This method provides these points that must be added, assuming the original segment doesn't
17440 start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by
17441 \ref getTraverseCornerPoints.)
17443 For example, consider a segment which directly goes from region 4 to 2 but originally is far out
17444 to the top left such that it doesn't cross region 5. Naively optimizing these points by
17445 projecting them on the top and left borders of region 5 will create a segment that surely crosses
17446 5, creating a visual artifact in the plot. This method prevents this by providing extra points at
17447 the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without
17448 traversing 5.
17450 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
17452 QVector<QPointF> result;
17453 switch (prevRegion)
17455 case 1:
17457 switch (currentRegion)
17459 case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17460 case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17461 case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17462 case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17463 case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17464 case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17465 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
17466 if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17467 { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17468 else
17469 { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17470 break;
17473 break;
17475 case 2:
17477 switch (currentRegion)
17479 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17480 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17481 case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17482 case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17483 case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17484 case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17486 break;
17488 case 3:
17490 switch (currentRegion)
17492 case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17493 case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17494 case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17495 case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17496 case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17497 case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17498 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
17499 if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17500 { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17501 else
17502 { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17503 break;
17506 break;
17508 case 4:
17510 switch (currentRegion)
17512 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17513 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17514 case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17515 case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17516 case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17517 case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17519 break;
17521 case 5:
17523 switch (currentRegion)
17525 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17526 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17527 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17528 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17530 break;
17532 case 6:
17534 switch (currentRegion)
17536 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17537 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17538 case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17539 case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17540 case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17541 case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17543 break;
17545 case 7:
17547 switch (currentRegion)
17549 case 4: { result << coordsToPixels(rectRight, rectTop); break; }
17550 case 8: { result << coordsToPixels(rectRight, rectTop); break; }
17551 case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
17552 case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
17553 case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17554 case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17555 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
17556 if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17557 { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17558 else
17559 { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17560 break;
17563 break;
17565 case 8:
17567 switch (currentRegion)
17569 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17570 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17571 case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17572 case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17573 case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17574 case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17576 break;
17578 case 9:
17580 switch (currentRegion)
17582 case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
17583 case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
17584 case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
17585 case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
17586 case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17587 case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17588 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
17589 if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17590 { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17591 else
17592 { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17593 break;
17596 break;
17599 return result;
17602 /*! \internal
17604 This function is part of the curve optimization algorithm of \ref getCurveData.
17606 This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref
17607 getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion
17608 nor \a currentRegion is 5 itself.
17610 If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the
17611 segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref
17612 getTraverse).
17614 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
17616 switch (prevRegion)
17618 case 1:
17620 switch (currentRegion)
17622 case 4:
17623 case 7:
17624 case 2:
17625 case 3: return false;
17626 default: return true;
17629 case 2:
17631 switch (currentRegion)
17633 case 1:
17634 case 3: return false;
17635 default: return true;
17638 case 3:
17640 switch (currentRegion)
17642 case 1:
17643 case 2:
17644 case 6:
17645 case 9: return false;
17646 default: return true;
17649 case 4:
17651 switch (currentRegion)
17653 case 1:
17654 case 7: return false;
17655 default: return true;
17658 case 5: return false; // should never occur
17659 case 6:
17661 switch (currentRegion)
17663 case 3:
17664 case 9: return false;
17665 default: return true;
17668 case 7:
17670 switch (currentRegion)
17672 case 1:
17673 case 4:
17674 case 8:
17675 case 9: return false;
17676 default: return true;
17679 case 8:
17681 switch (currentRegion)
17683 case 7:
17684 case 9: return false;
17685 default: return true;
17688 case 9:
17690 switch (currentRegion)
17692 case 3:
17693 case 6:
17694 case 8:
17695 case 7: return false;
17696 default: return true;
17699 default: return true;
17704 /*! \internal
17706 This function is part of the curve optimization algorithm of \ref getCurveData.
17708 This method assumes that the \ref mayTraverse test has returned true, so there is a chance the
17709 segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible
17710 region 5.
17712 The return value of this method indicates whether the segment actually traverses region 5 or not.
17714 If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and
17715 exit points of region 5. They will become the optimized points for that segment.
17717 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
17719 QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
17720 if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
17722 // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17723 intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
17724 intersections.append(QPointF(key, rectTop));
17725 } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
17727 // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17728 intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
17729 intersections.append(QPointF(rectRight, value));
17730 } else // line is skewed
17732 double gamma;
17733 double keyPerValue = (key-prevKey)/(value-prevValue);
17734 // check top of rect:
17735 gamma = prevKey + (rectTop-prevValue)*keyPerValue;
17736 if (gamma >= rectLeft && gamma <= rectRight)
17737 intersections.append(QPointF(gamma, rectTop));
17738 // check bottom of rect:
17739 gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
17740 if (gamma >= rectLeft && gamma <= rectRight)
17741 intersections.append(QPointF(gamma, rectBottom));
17742 double valuePerKey = 1.0/keyPerValue;
17743 // check left of rect:
17744 gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
17745 if (gamma >= rectBottom && gamma <= rectTop)
17746 intersections.append(QPointF(rectLeft, gamma));
17747 // check right of rect:
17748 gamma = prevValue + (rectRight-prevKey)*valuePerKey;
17749 if (gamma >= rectBottom && gamma <= rectTop)
17750 intersections.append(QPointF(rectRight, gamma));
17753 // handle cases where found points isn't exactly 2:
17754 if (intersections.size() > 2)
17756 // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
17757 double distSqrMax = 0;
17758 QPointF pv1, pv2;
17759 for (int i=0; i<intersections.size()-1; ++i)
17761 for (int k=i+1; k<intersections.size(); ++k)
17763 QPointF distPoint = intersections.at(i)-intersections.at(k);
17764 double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
17765 if (distSqr > distSqrMax)
17767 pv1 = intersections.at(i);
17768 pv2 = intersections.at(k);
17769 distSqrMax = distSqr;
17773 intersections = QList<QPointF>() << pv1 << pv2;
17774 } else if (intersections.size() != 2)
17776 // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
17777 return false;
17780 // possibly re-sort points so optimized point segment has same direction as original segment:
17781 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
17782 intersections.move(0, 1);
17783 crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
17784 crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
17785 return true;
17788 /*! \internal
17790 This function is part of the curve optimization algorithm of \ref getCurveData.
17792 This method assumes that the \ref getTraverse test has returned true, so the segment definitely
17793 traverses the visible region 5 when going from \a prevRegion to \a currentRegion.
17795 In certain situations it is not sufficient to merely generate the entry and exit points of the
17796 segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in
17797 addition to traversing region 5, skips another region outside of region 5, which makes it
17798 necessary to add an optimized corner point there (very similar to the job \ref
17799 getOptimizedCornerPoints does for segments that are completely in outside regions and don't
17800 traverse 5).
17802 As an example, consider a segment going from region 1 to region 6, traversing the lower left
17803 corner of region 5. In this configuration, the segment additionally crosses the border between
17804 region 1 and 2 before entering region 5. This makes it necessary to add an additional point in
17805 the top left corner, before adding the optimized traverse points. So in this case, the output
17806 parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be
17807 empty.
17809 In some cases, such as when going from region 1 to 9, it may even be necessary to add additional
17810 corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse
17811 return the respective corner points.
17813 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
17815 switch (prevRegion)
17817 case 1:
17819 switch (currentRegion)
17821 case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17822 case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17823 case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17825 break;
17827 case 2:
17829 switch (currentRegion)
17831 case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17832 case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17834 break;
17836 case 3:
17838 switch (currentRegion)
17840 case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17841 case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17842 case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17844 break;
17846 case 4:
17848 switch (currentRegion)
17850 case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17851 case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17853 break;
17855 case 5: { break; } // shouldn't happen because this method only handles full traverses
17856 case 6:
17858 switch (currentRegion)
17860 case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17861 case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17863 break;
17865 case 7:
17867 switch (currentRegion)
17869 case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17870 case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17871 case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17873 break;
17875 case 8:
17877 switch (currentRegion)
17879 case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17880 case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17882 break;
17884 case 9:
17886 switch (currentRegion)
17888 case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17889 case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17890 case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17892 break;
17897 /*! \internal
17899 Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
17900 pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
17901 \ref selectTest.
17903 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17905 if (mData->isEmpty())
17907 qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17908 return 500;
17910 if (mData->size() == 1)
17912 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17913 return QVector2D(dataPoint-pixelPoint).length();
17916 // calculate minimum distance to line segments:
17917 QVector<QPointF> *lineData = new QVector<QPointF>;
17918 getCurveData(lineData);
17919 double minDistSqr = std::numeric_limits<double>::max();
17920 for (int i=0; i<lineData->size()-1; ++i)
17922 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17923 if (currentDistSqr < minDistSqr)
17924 minDistSqr = currentDistSqr;
17926 delete lineData;
17927 return qSqrt(minDistSqr);
17930 /* inherits documentation from base class */
17931 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17933 QCPRange range;
17934 bool haveLower = false;
17935 bool haveUpper = false;
17937 double current;
17939 QCPCurveDataMap::const_iterator it = mData->constBegin();
17940 while (it != mData->constEnd())
17942 current = it.value().key;
17943 if (!qIsNaN(current) && !qIsNaN(it.value().value))
17945 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17947 if (current < range.lower || !haveLower)
17949 range.lower = current;
17950 haveLower = true;
17952 if (current > range.upper || !haveUpper)
17954 range.upper = current;
17955 haveUpper = true;
17959 ++it;
17962 foundRange = haveLower && haveUpper;
17963 return range;
17966 /* inherits documentation from base class */
17967 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17969 QCPRange range;
17970 bool haveLower = false;
17971 bool haveUpper = false;
17973 double current;
17975 QCPCurveDataMap::const_iterator it = mData->constBegin();
17976 while (it != mData->constEnd())
17978 current = it.value().value;
17979 if (!qIsNaN(current) && !qIsNaN(it.value().key))
17981 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17983 if (current < range.lower || !haveLower)
17985 range.lower = current;
17986 haveLower = true;
17988 if (current > range.upper || !haveUpper)
17990 range.upper = current;
17991 haveUpper = true;
17995 ++it;
17998 foundRange = haveLower && haveUpper;
17999 return range;
18003 ////////////////////////////////////////////////////////////////////////////////////////////////////
18004 //////////////////// QCPBarsGroup
18005 ////////////////////////////////////////////////////////////////////////////////////////////////////
18007 /*! \class QCPBarsGroup
18008 \brief Groups multiple QCPBars together so they appear side by side
18010 \image html QCPBarsGroup.png
18012 When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable
18013 to have them appearing next to each other at each key. This is what adding the respective QCPBars
18014 plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each
18015 other, see \ref QCPBars::moveAbove.)
18017 \section qcpbarsgroup-usage Usage
18019 To add a QCPBars plottable to the group, create a new group and then add the respective bars
18020 intances:
18021 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation
18022 Alternatively to appending to the group like shown above, you can also set the group on the
18023 QCPBars plottable via \ref QCPBars::setBarsGroup.
18025 The spacing between the bars can be configured via \ref setSpacingType and \ref setSpacing. The
18026 bars in this group appear in the plot in the order they were appended. To insert a bars plottable
18027 at a certain index position, or to reposition a bars plottable which is already in the group, use
18028 \ref insert.
18030 To remove specific bars from the group, use either \ref remove or call \ref
18031 QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable.
18033 To clear the entire group, call \ref clear, or simply delete the group.
18035 \section qcpbarsgroup-example Example
18037 The image above is generated with the following code:
18038 \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example
18041 /* start of documentation of inline functions */
18043 /*! \fn QList<QCPBars*> QCPBarsGroup::bars() const
18045 Returns all bars currently in this group.
18047 \see bars(int index)
18050 /*! \fn int QCPBarsGroup::size() const
18052 Returns the number of QCPBars plottables that are part of this group.
18056 /*! \fn bool QCPBarsGroup::isEmpty() const
18058 Returns whether this bars group is empty.
18060 \see size
18063 /*! \fn bool QCPBarsGroup::contains(QCPBars *bars)
18065 Returns whether the specified \a bars plottable is part of this group.
18069 /* end of documentation of inline functions */
18072 Constructs a new bars group for the specified QCustomPlot instance.
18074 QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :
18075 QObject(parentPlot),
18076 mParentPlot(parentPlot),
18077 mSpacingType(stAbsolute),
18078 mSpacing(4)
18082 QCPBarsGroup::~QCPBarsGroup()
18084 clear();
18088 Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType.
18090 The actual spacing can then be specified with \ref setSpacing.
18092 \see setSpacing
18094 void QCPBarsGroup::setSpacingType(SpacingType spacingType)
18096 mSpacingType = spacingType;
18100 Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is
18101 defined by the current \ref SpacingType, which can be set with \ref setSpacingType.
18103 \see setSpacingType
18105 void QCPBarsGroup::setSpacing(double spacing)
18107 mSpacing = spacing;
18111 Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars
18112 exists, returns 0.
18114 \see bars(), size
18116 QCPBars *QCPBarsGroup::bars(int index) const
18118 if (index >= 0 && index < mBars.size())
18120 return mBars.at(index);
18121 } else
18123 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18124 return 0;
18129 Removes all QCPBars plottables from this group.
18131 \see isEmpty
18133 void QCPBarsGroup::clear()
18135 foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
18136 bars->setBarsGroup(0); // removes itself via removeBars
18140 Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref
18141 QCPBars::setBarsGroup on the \a bars instance.
18143 \see insert, remove
18145 void QCPBarsGroup::append(QCPBars *bars)
18147 if (!bars)
18149 qDebug() << Q_FUNC_INFO << "bars is 0";
18150 return;
18153 if (!mBars.contains(bars))
18154 bars->setBarsGroup(this);
18155 else
18156 qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18160 Inserts the specified \a bars plottable into this group at the specified index position \a i.
18161 This gives you full control over the ordering of the bars.
18163 \a bars may already be part of this group. In that case, \a bars is just moved to the new index
18164 position.
18166 \see append, remove
18168 void QCPBarsGroup::insert(int i, QCPBars *bars)
18170 if (!bars)
18172 qDebug() << Q_FUNC_INFO << "bars is 0";
18173 return;
18176 // first append to bars list normally:
18177 if (!mBars.contains(bars))
18178 bars->setBarsGroup(this);
18179 // then move to according position:
18180 mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18184 Removes the specified \a bars plottable from this group.
18186 \see contains, clear
18188 void QCPBarsGroup::remove(QCPBars *bars)
18190 if (!bars)
18192 qDebug() << Q_FUNC_INFO << "bars is 0";
18193 return;
18196 if (mBars.contains(bars))
18197 bars->setBarsGroup(0);
18198 else
18199 qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18202 /*! \internal
18204 Adds the specified \a bars to the internal mBars list of bars. This method does not change the
18205 barsGroup property on \a bars.
18207 \see unregisterBars
18209 void QCPBarsGroup::registerBars(QCPBars *bars)
18211 if (!mBars.contains(bars))
18212 mBars.append(bars);
18215 /*! \internal
18217 Removes the specified \a bars from the internal mBars list of bars. This method does not change
18218 the barsGroup property on \a bars.
18220 \see registerBars
18222 void QCPBarsGroup::unregisterBars(QCPBars *bars)
18224 mBars.removeOne(bars);
18227 /*! \internal
18229 Returns the pixel offset in the key dimension the specified \a bars plottable should have at the
18230 given key coordinate \a keyCoord. The offset is relative to the pixel position of the key
18231 coordinate \a keyCoord.
18233 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18235 // find list of all base bars in case some mBars are stacked:
18236 QList<const QCPBars*> baseBars;
18237 foreach (const QCPBars *b, mBars)
18239 while (b->barBelow())
18240 b = b->barBelow();
18241 if (!baseBars.contains(b))
18242 baseBars.append(b);
18244 // find base bar this "bars" is stacked on:
18245 const QCPBars *thisBase = bars;
18246 while (thisBase->barBelow())
18247 thisBase = thisBase->barBelow();
18249 // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18250 double result = 0;
18251 int index = baseBars.indexOf(thisBase);
18252 if (index >= 0)
18254 int startIndex;
18255 double lowerPixelWidth, upperPixelWidth;
18256 if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18258 return result;
18259 } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18261 if (baseBars.size() % 2 == 0) // even number of bars
18263 startIndex = baseBars.size()/2-1;
18264 result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18265 } else // uneven number of bars
18267 startIndex = (baseBars.size()-1)/2-1;
18268 baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18269 result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18270 result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18272 for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18274 baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18275 result -= qAbs(upperPixelWidth-lowerPixelWidth);
18276 result -= getPixelSpacing(baseBars.at(i), keyCoord);
18278 // finally half of our bars width:
18279 baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18280 result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18281 } else // bar is to the right of center
18283 if (baseBars.size() % 2 == 0) // even number of bars
18285 startIndex = baseBars.size()/2;
18286 result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18287 } else // uneven number of bars
18289 startIndex = (baseBars.size()-1)/2+1;
18290 baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18291 result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18292 result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18294 for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18296 baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18297 result += qAbs(upperPixelWidth-lowerPixelWidth);
18298 result += getPixelSpacing(baseBars.at(i), keyCoord);
18300 // finally half of our bars width:
18301 baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18302 result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18305 return result;
18308 /*! \internal
18310 Returns the spacing in pixels which is between this \a bars and the following one, both at the
18311 key coordinate \a keyCoord.
18313 \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only
18314 needed to get acces to the key axis transformation and axis rect for the modes \ref
18315 stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in
18316 \ref stPlotCoords on a logarithmic axis.
18318 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18320 switch (mSpacingType)
18322 case stAbsolute:
18324 return mSpacing;
18326 case stAxisRectRatio:
18328 if (bars->keyAxis()->orientation() == Qt::Horizontal)
18329 return bars->keyAxis()->axisRect()->width()*mSpacing;
18330 else
18331 return bars->keyAxis()->axisRect()->height()*mSpacing;
18333 case stPlotCoords:
18335 double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18336 return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18339 return 0;
18343 ////////////////////////////////////////////////////////////////////////////////////////////////////
18344 //////////////////// QCPBarData
18345 ////////////////////////////////////////////////////////////////////////////////////////////////////
18347 /*! \class QCPBarData
18348 \brief Holds the data of one single data point (one bar) for QCPBars.
18350 The container for storing multiple data points is \ref QCPBarDataMap.
18352 The stored data is:
18353 \li \a key: coordinate on the key axis of this bar
18354 \li \a value: height coordinate on the value axis of this bar
18356 \see QCPBarDataaMap
18360 Constructs a bar data point with key and value set to zero.
18362 QCPBarData::QCPBarData() :
18363 key(0),
18364 value(0)
18369 Constructs a bar data point with the specified \a key and \a value.
18371 QCPBarData::QCPBarData(double key, double value) :
18372 key(key),
18373 value(value)
18378 ////////////////////////////////////////////////////////////////////////////////////////////////////
18379 //////////////////// QCPBars
18380 ////////////////////////////////////////////////////////////////////////////////////////////////////
18382 /*! \class QCPBars
18383 \brief A plottable representing a bar chart in a plot.
18385 \image html QCPBars.png
18387 To plot data, assign it with the \ref setData or \ref addData functions.
18389 \section appearance Changing the appearance
18391 The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
18392 The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth.
18394 Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other
18395 (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear
18396 stacked.
18398 If you would like to group multiple QCPBars plottables together so they appear side by side as
18399 shown below, use QCPBarsGroup.
18401 \image html QCPBarsGroup.png
18403 \section usage Usage
18405 Like all data representing objects in QCustomPlot, the QCPBars is a plottable
18406 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
18407 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
18409 Usually, you first create an instance:
18410 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1
18411 add it to the customPlot with QCustomPlot::addPlottable:
18412 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2
18413 and then modify the properties of the newly created plottable, e.g.:
18414 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3
18417 /* start of documentation of inline functions */
18419 /*! \fn QCPBars *QCPBars::barBelow() const
18420 Returns the bars plottable that is directly below this bars plottable.
18421 If there is no such plottable, returns 0.
18423 \see barAbove, moveBelow, moveAbove
18426 /*! \fn QCPBars *QCPBars::barAbove() const
18427 Returns the bars plottable that is directly above this bars plottable.
18428 If there is no such plottable, returns 0.
18430 \see barBelow, moveBelow, moveAbove
18433 /* end of documentation of inline functions */
18436 Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
18437 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
18438 the same orientation. If either of these restrictions is violated, a corresponding message is
18439 printed to the debug output (qDebug), the construction is not aborted, though.
18441 The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
18442 then takes ownership of the bar chart.
18444 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18445 QCPAbstractPlottable(keyAxis, valueAxis),
18446 mData(new QCPBarDataMap),
18447 mWidth(0.75),
18448 mWidthType(wtPlotCoords),
18449 mBarsGroup(0),
18450 mBaseValue(0)
18452 // modify inherited properties from abstract plottable:
18453 mPen.setColor(Qt::blue);
18454 mPen.setStyle(Qt::SolidLine);
18455 mBrush.setColor(QColor(40, 50, 255, 30));
18456 mBrush.setStyle(Qt::SolidPattern);
18457 mSelectedPen = mPen;
18458 mSelectedPen.setWidthF(2.5);
18459 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18460 mSelectedBrush = mBrush;
18463 QCPBars::~QCPBars()
18465 setBarsGroup(0);
18466 if (mBarBelow || mBarAbove)
18467 connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18468 delete mData;
18472 Sets the width of the bars.
18474 How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...),
18475 depends on the currently set width type, see \ref setWidthType and \ref WidthType.
18477 void QCPBars::setWidth(double width)
18479 mWidth = width;
18483 Sets how the width of the bars is defined. See the documentation of \ref WidthType for an
18484 explanation of the possible values for \a widthType.
18486 The default value is \ref wtPlotCoords.
18488 \see setWidth
18490 void QCPBars::setWidthType(QCPBars::WidthType widthType)
18492 mWidthType = widthType;
18496 Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref
18497 QCPBarsGroup::append.
18499 To remove this QCPBars from any group, set \a barsGroup to 0.
18501 void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
18503 // deregister at old group:
18504 if (mBarsGroup)
18505 mBarsGroup->unregisterBars(this);
18506 mBarsGroup = barsGroup;
18507 // register at new group:
18508 if (mBarsGroup)
18509 mBarsGroup->registerBars(this);
18513 Sets the base value of this bars plottable.
18515 The base value defines where on the value coordinate the bars start. How far the bars extend from
18516 the base value is given by their individual value data. For example, if the base value is set to
18517 1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at
18520 For stacked bars, only the base value of the bottom-most QCPBars has meaning.
18522 The default base value is 0.
18524 void QCPBars::setBaseValue(double baseValue)
18526 mBaseValue = baseValue;
18530 Replaces the current data with the provided \a data.
18532 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
18533 takes ownership of the passed data and replaces the internal data pointer with it. This is
18534 significantly faster than copying for large datasets.
18536 void QCPBars::setData(QCPBarDataMap *data, bool copy)
18538 if (mData == data)
18540 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
18541 return;
18543 if (copy)
18545 *mData = *data;
18546 } else
18548 delete mData;
18549 mData = data;
18553 /*! \overload
18555 Replaces the current data with the provided points in \a key and \a value tuples. The
18556 provided vectors should have equal length. Else, the number of added points will be the size of
18557 the smallest vector.
18559 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
18561 mData->clear();
18562 int n = key.size();
18563 n = qMin(n, value.size());
18564 QCPBarData newData;
18565 for (int i=0; i<n; ++i)
18567 newData.key = key[i];
18568 newData.value = value[i];
18569 mData->insertMulti(newData.key, newData);
18574 Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
18575 below the bars of \a bars. The move target \a bars must use the same key and value axis as this
18576 plottable.
18578 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18579 has a bars object below itself, this bars object is inserted between the two. If this bars object
18580 is already between two other bars, the two other bars will be stacked on top of each other after
18581 the operation.
18583 To remove this bars plottable from any stacking, set \a bars to 0.
18585 \see moveBelow, barAbove, barBelow
18587 void QCPBars::moveBelow(QCPBars *bars)
18589 if (bars == this) return;
18590 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18592 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18593 return;
18595 // remove from stacking:
18596 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18597 // if new bar given, insert this bar below it:
18598 if (bars)
18600 if (bars->mBarBelow)
18601 connectBars(bars->mBarBelow.data(), this);
18602 connectBars(this, bars);
18607 Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
18608 above the bars of \a bars. The move target \a bars must use the same key and value axis as this
18609 plottable.
18611 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18612 has a bars object below itself, this bars object is inserted between the two. If this bars object
18613 is already between two other bars, the two other bars will be stacked on top of each other after
18614 the operation.
18616 To remove this bars plottable from any stacking, set \a bars to 0.
18618 \see moveBelow, barBelow, barAbove
18620 void QCPBars::moveAbove(QCPBars *bars)
18622 if (bars == this) return;
18623 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18625 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18626 return;
18628 // remove from stacking:
18629 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18630 // if new bar given, insert this bar above it:
18631 if (bars)
18633 if (bars->mBarAbove)
18634 connectBars(this, bars->mBarAbove.data());
18635 connectBars(bars, this);
18640 Adds the provided data points in \a dataMap to the current data.
18641 \see removeData
18643 void QCPBars::addData(const QCPBarDataMap &dataMap)
18645 mData->unite(dataMap);
18648 /*! \overload
18649 Adds the provided single data point in \a data to the current data.
18650 \see removeData
18652 void QCPBars::addData(const QCPBarData &data)
18654 mData->insertMulti(data.key, data);
18657 /*! \overload
18658 Adds the provided single data point as \a key and \a value tuple to the current data
18659 \see removeData
18661 void QCPBars::addData(double key, double value)
18663 QCPBarData newData;
18664 newData.key = key;
18665 newData.value = value;
18666 mData->insertMulti(newData.key, newData);
18669 /*! \overload
18670 Adds the provided data points as \a key and \a value tuples to the current data.
18671 \see removeData
18673 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
18675 int n = keys.size();
18676 n = qMin(n, values.size());
18677 QCPBarData newData;
18678 for (int i=0; i<n; ++i)
18680 newData.key = keys[i];
18681 newData.value = values[i];
18682 mData->insertMulti(newData.key, newData);
18687 Removes all data points with key smaller than \a key.
18688 \see addData, clearData
18690 void QCPBars::removeDataBefore(double key)
18692 QCPBarDataMap::iterator it = mData->begin();
18693 while (it != mData->end() && it.key() < key)
18694 it = mData->erase(it);
18698 Removes all data points with key greater than \a key.
18699 \see addData, clearData
18701 void QCPBars::removeDataAfter(double key)
18703 if (mData->isEmpty()) return;
18704 QCPBarDataMap::iterator it = mData->upperBound(key);
18705 while (it != mData->end())
18706 it = mData->erase(it);
18710 Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
18711 greater or equal to \a toKey, the function does nothing. To remove a single data point with known
18712 key, use \ref removeData(double key).
18714 \see addData, clearData
18716 void QCPBars::removeData(double fromKey, double toKey)
18718 if (fromKey >= toKey || mData->isEmpty()) return;
18719 QCPBarDataMap::iterator it = mData->upperBound(fromKey);
18720 QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
18721 while (it != itEnd)
18722 it = mData->erase(it);
18725 /*! \overload
18727 Removes a single data point at \a key. If the position is not known with absolute precision,
18728 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
18729 around the suspected position, depeding on the precision with which the key is known.
18731 \see addData, clearData
18733 void QCPBars::removeData(double key)
18735 mData->remove(key);
18739 Removes all data points.
18740 \see removeData, removeDataAfter, removeDataBefore
18742 void QCPBars::clearData()
18744 mData->clear();
18747 /* inherits documentation from base class */
18748 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18750 Q_UNUSED(details)
18751 if (onlySelectable && !mSelectable)
18752 return -1;
18753 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18755 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18757 QCPBarDataMap::ConstIterator it;
18758 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
18760 if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
18761 return mParentPlot->selectionTolerance()*0.99;
18764 return -1;
18767 /* inherits documentation from base class */
18768 void QCPBars::draw(QCPPainter *painter)
18770 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18771 if (mData->isEmpty()) return;
18773 QCPBarDataMap::const_iterator it, lower, upperEnd;
18774 getVisibleDataBounds(lower, upperEnd);
18775 for (it = lower; it != upperEnd; ++it)
18777 // check data validity if flag set:
18778 #ifdef QCUSTOMPLOT_CHECK_DATA
18779 if (QCP::isInvalidData(it.value().key, it.value().value))
18780 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
18781 #endif
18782 QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
18783 // draw bar fill:
18784 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
18786 applyFillAntialiasingHint(painter);
18787 painter->setPen(Qt::NoPen);
18788 painter->setBrush(mainBrush());
18789 painter->drawPolygon(barPolygon);
18791 // draw bar line:
18792 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
18794 applyDefaultAntialiasingHint(painter);
18795 painter->setPen(mainPen());
18796 painter->setBrush(Qt::NoBrush);
18797 painter->drawPolyline(barPolygon);
18802 /* inherits documentation from base class */
18803 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18805 // draw filled rect:
18806 applyDefaultAntialiasingHint(painter);
18807 painter->setBrush(mBrush);
18808 painter->setPen(mPen);
18809 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18810 r.moveCenter(rect.center());
18811 painter->drawRect(r);
18814 /*! \internal
18816 called by \ref draw to determine which data (key) range is visible at the current key axis range
18817 setting, so only that needs to be processed. It also takes into account the bar width.
18819 \a lower returns an iterator to the lowest data point that needs to be taken into account when
18820 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
18821 lower may still be just outside the visible range.
18823 \a upperEnd returns an iterator one higher than the highest visible data point. Same as before, \a
18824 upperEnd may also lie just outside of the visible range.
18826 if the bars plottable contains no data, both \a lower and \a upperEnd point to constEnd.
18828 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
18830 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
18831 if (mData->isEmpty())
18833 lower = mData->constEnd();
18834 upperEnd = mData->constEnd();
18835 return;
18838 // get visible data range as QMap iterators
18839 lower = mData->lowerBound(mKeyAxis.data()->range().lower);
18840 upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
18841 double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
18842 double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
18843 bool isVisible = false;
18844 // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
18845 QCPBarDataMap::const_iterator it = lower;
18846 while (it != mData->constBegin())
18848 --it;
18849 QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
18850 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18851 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
18852 else // keyaxis is vertical
18853 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
18854 if (isVisible)
18855 lower = it;
18856 else
18857 break;
18859 // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
18860 it = upperEnd;
18861 while (it != mData->constEnd())
18863 QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
18864 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18865 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
18866 else // keyaxis is vertical
18867 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
18868 if (isVisible)
18869 upperEnd = it+1;
18870 else
18871 break;
18872 ++it;
18876 /*! \internal
18878 Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
18879 and shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref
18880 setBaseValue).
18882 QPolygonF QCPBars::getBarPolygon(double key, double value) const
18884 QCPAxis *keyAxis = mKeyAxis.data();
18885 QCPAxis *valueAxis = mValueAxis.data();
18886 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
18888 QPolygonF result;
18889 double lowerPixelWidth, upperPixelWidth;
18890 getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
18891 double base = getStackedBaseValue(key, value >= 0);
18892 double basePixel = valueAxis->coordToPixel(base);
18893 double valuePixel = valueAxis->coordToPixel(base+value);
18894 double keyPixel = keyAxis->coordToPixel(key);
18895 if (mBarsGroup)
18896 keyPixel += mBarsGroup->keyPixelOffset(this, key);
18897 if (keyAxis->orientation() == Qt::Horizontal)
18899 result << QPointF(keyPixel+lowerPixelWidth, basePixel);
18900 result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
18901 result << QPointF(keyPixel+upperPixelWidth, valuePixel);
18902 result << QPointF(keyPixel+upperPixelWidth, basePixel);
18903 } else
18905 result << QPointF(basePixel, keyPixel+lowerPixelWidth);
18906 result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
18907 result << QPointF(valuePixel, keyPixel+upperPixelWidth);
18908 result << QPointF(basePixel, keyPixel+upperPixelWidth);
18910 return result;
18913 /*! \internal
18915 This function is used to determine the width of the bar at coordinate \a key, according to the
18916 specified width (\ref setWidth) and width type (\ref setWidthType).
18918 The output parameters \a lower and \a upper return the number of pixels the bar extends to lower
18919 and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a
18920 lower is negative and \a upper positive).
18922 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
18924 switch (mWidthType)
18926 case wtAbsolute:
18928 upper = mWidth*0.5;
18929 lower = -upper;
18930 if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18931 qSwap(lower, upper);
18932 break;
18934 case wtAxisRectRatio:
18936 if (mKeyAxis && mKeyAxis.data()->axisRect())
18938 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18939 upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
18940 else
18941 upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
18942 lower = -upper;
18943 if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18944 qSwap(lower, upper);
18945 } else
18946 qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
18947 break;
18949 case wtPlotCoords:
18951 if (mKeyAxis)
18953 double keyPixel = mKeyAxis.data()->coordToPixel(key);
18954 upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
18955 lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
18956 // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
18957 // coordinate transform which includes range direction
18958 } else
18959 qDebug() << Q_FUNC_INFO << "No key axis defined";
18960 break;
18965 /*! \internal
18967 This function is called to find at which value to start drawing the base of a bar at \a key, when
18968 it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
18970 positive and negative bars are separated per stack (positive are stacked above baseValue upwards,
18971 negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the
18972 bar for which we need the base value is negative, set \a positive to false.
18974 double QCPBars::getStackedBaseValue(double key, bool positive) const
18976 if (mBarBelow)
18978 double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
18979 // find bars of mBarBelow that are approximately at key and find largest one:
18980 double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
18981 if (key == 0)
18982 epsilon = 1e-6;
18983 QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
18984 QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
18985 while (it != itEnd)
18987 if ((positive && it.value().value > max) ||
18988 (!positive && it.value().value < max))
18989 max = it.value().value;
18990 ++it;
18992 // recurse down the bar-stack to find the total height:
18993 return max + mBarBelow.data()->getStackedBaseValue(key, positive);
18994 } else
18995 return mBaseValue;
18998 /*! \internal
19000 Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s)
19001 currently above lower and below upper will become disconnected to lower/upper.
19003 If lower is zero, upper will be disconnected at the bottom.
19004 If upper is zero, lower will be disconnected at the top.
19006 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
19008 if (!lower && !upper) return;
19010 if (!lower) // disconnect upper at bottom
19012 // disconnect old bar below upper:
19013 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19014 upper->mBarBelow.data()->mBarAbove = 0;
19015 upper->mBarBelow = 0;
19016 } else if (!upper) // disconnect lower at top
19018 // disconnect old bar above lower:
19019 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19020 lower->mBarAbove.data()->mBarBelow = 0;
19021 lower->mBarAbove = 0;
19022 } else // connect lower and upper
19024 // disconnect old bar above lower:
19025 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19026 lower->mBarAbove.data()->mBarBelow = 0;
19027 // disconnect old bar below upper:
19028 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19029 upper->mBarBelow.data()->mBarAbove = 0;
19030 lower->mBarAbove = upper;
19031 upper->mBarBelow = lower;
19035 /* inherits documentation from base class */
19036 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19038 QCPRange range;
19039 bool haveLower = false;
19040 bool haveUpper = false;
19042 double current;
19043 QCPBarDataMap::const_iterator it = mData->constBegin();
19044 while (it != mData->constEnd())
19046 current = it.value().key;
19047 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19049 if (current < range.lower || !haveLower)
19051 range.lower = current;
19052 haveLower = true;
19054 if (current > range.upper || !haveUpper)
19056 range.upper = current;
19057 haveUpper = true;
19060 ++it;
19062 // determine exact range of bars by including bar width and barsgroup offset:
19063 if (haveLower && mKeyAxis)
19065 double lowerPixelWidth, upperPixelWidth, keyPixel;
19066 getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19067 keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19068 if (mBarsGroup)
19069 keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19070 range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19072 if (haveUpper && mKeyAxis)
19074 double lowerPixelWidth, upperPixelWidth, keyPixel;
19075 getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19076 keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19077 if (mBarsGroup)
19078 keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19079 range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19081 foundRange = haveLower && haveUpper;
19082 return range;
19085 /* inherits documentation from base class */
19086 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19088 QCPRange range;
19089 range.lower = mBaseValue;
19090 range.upper = mBaseValue;
19091 bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19092 bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19093 double current;
19095 QCPBarDataMap::const_iterator it = mData->constBegin();
19096 while (it != mData->constEnd())
19098 current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19099 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19101 if (current < range.lower || !haveLower)
19103 range.lower = current;
19104 haveLower = true;
19106 if (current > range.upper || !haveUpper)
19108 range.upper = current;
19109 haveUpper = true;
19112 ++it;
19115 foundRange = true; // return true because bar charts always have the 0-line visible
19116 return range;
19120 ////////////////////////////////////////////////////////////////////////////////////////////////////
19121 //////////////////// QCPStatisticalBox
19122 ////////////////////////////////////////////////////////////////////////////////////////////////////
19124 /*! \class QCPStatisticalBox
19125 \brief A plottable representing a single statistical box in a plot.
19127 \image html QCPStatisticalBox.png
19129 To plot data, assign it with the individual parameter functions or use \ref setData to set all
19130 parameters at once. The individual functions are:
19131 \li \ref setMinimum
19132 \li \ref setLowerQuartile
19133 \li \ref setMedian
19134 \li \ref setUpperQuartile
19135 \li \ref setMaximum
19137 Additionally you can define a list of outliers, drawn as scatter datapoints:
19138 \li \ref setOutliers
19140 \section appearance Changing the appearance
19142 The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You may change
19143 the width of the box with \ref setWidth in plot coordinates (not pixels).
19145 Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
19146 setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
19147 (maximum) and bottom (minimum).
19149 The median indicator line has its own pen, \ref setMedianPen.
19151 If the whisker backbone pen is changed, make sure to set the capStyle to Qt::FlatCap. Else, the
19152 backbone line might exceed the whisker bars by a few pixels due to the pen cap being not
19153 perfectly flat.
19155 The Outlier data points are drawn as normal scatter points. Their look can be controlled with
19156 \ref setOutlierStyle
19158 \section usage Usage
19160 Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable.
19161 Usually, you first create an instance and add it to the customPlot:
19162 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1
19163 and then modify the properties of the newly created plottable, e.g.:
19164 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2
19168 Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
19169 value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
19170 not have the same orientation. If either of these restrictions is violated, a corresponding
19171 message is printed to the debug output (qDebug), the construction is not aborted, though.
19173 The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
19174 QCustomPlot then takes ownership of the statistical box.
19176 QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
19177 QCPAbstractPlottable(keyAxis, valueAxis),
19178 mKey(0),
19179 mMinimum(0),
19180 mLowerQuartile(0),
19181 mMedian(0),
19182 mUpperQuartile(0),
19183 mMaximum(0)
19185 setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
19186 setWhiskerWidth(0.2);
19187 setWidth(0.5);
19189 setPen(QPen(Qt::black));
19190 setSelectedPen(QPen(Qt::blue, 2.5));
19191 setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19192 setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19193 setWhiskerBarPen(QPen(Qt::black));
19194 setBrush(Qt::NoBrush);
19195 setSelectedBrush(Qt::NoBrush);
19199 Sets the key coordinate of the statistical box.
19201 void QCPStatisticalBox::setKey(double key)
19203 mKey = key;
19207 Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
19208 whisker, typically the minimum measurement of the sample that's not considered an outlier.
19210 \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19212 void QCPStatisticalBox::setMinimum(double value)
19214 mMinimum = value;
19218 Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
19219 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19220 sample, they contain 50% of the sample data.
19222 \see setUpperQuartile, setPen, setBrush, setWidth
19224 void QCPStatisticalBox::setLowerQuartile(double value)
19226 mLowerQuartile = value;
19230 Sets the parameter "median" of the statistical box plot. This is the value of the median mark
19231 inside the quartile box. The median separates the sample data in half (50% of the sample data is
19232 below/above the median).
19234 \see setMedianPen
19236 void QCPStatisticalBox::setMedian(double value)
19238 mMedian = value;
19242 Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
19243 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19244 sample, they contain 50% of the sample data.
19246 \see setLowerQuartile, setPen, setBrush, setWidth
19248 void QCPStatisticalBox::setUpperQuartile(double value)
19250 mUpperQuartile = value;
19254 Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
19255 whisker, typically the maximum measurement of the sample that's not considered an outlier.
19257 \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19259 void QCPStatisticalBox::setMaximum(double value)
19261 mMaximum = value;
19265 Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
19266 that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
19267 and displayed as such.
19269 \see setOutlierStyle
19271 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19273 mOutliers = values;
19277 Sets all parameters of the statistical box plot at once.
19279 \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
19281 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19283 setKey(key);
19284 setMinimum(minimum);
19285 setLowerQuartile(lowerQuartile);
19286 setMedian(median);
19287 setUpperQuartile(upperQuartile);
19288 setMaximum(maximum);
19292 Sets the width of the box in key coordinates.
19294 \see setWhiskerWidth
19296 void QCPStatisticalBox::setWidth(double width)
19298 mWidth = width;
19302 Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
19304 \see setWidth
19306 void QCPStatisticalBox::setWhiskerWidth(double width)
19308 mWhiskerWidth = width;
19312 Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
19314 Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker backbone from reaching
19315 a few pixels past the whisker bars, when using a non-zero pen width.
19317 \see setWhiskerBarPen
19319 void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
19321 mWhiskerPen = pen;
19325 Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
19326 each end of the whisker backbone).
19328 \see setWhiskerPen
19330 void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
19332 mWhiskerBarPen = pen;
19336 Sets the pen used for drawing the median indicator line inside the statistical box.
19338 void QCPStatisticalBox::setMedianPen(const QPen &pen)
19340 mMedianPen = pen;
19344 Sets the appearance of the outlier data points.
19346 \see setOutliers
19348 void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)
19350 mOutlierStyle = style;
19353 /* inherits documentation from base class */
19354 void QCPStatisticalBox::clearData()
19356 setOutliers(QVector<double>());
19357 setKey(0);
19358 setMinimum(0);
19359 setLowerQuartile(0);
19360 setMedian(0);
19361 setUpperQuartile(0);
19362 setMaximum(0);
19365 /* inherits documentation from base class */
19366 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19368 Q_UNUSED(details)
19369 if (onlySelectable && !mSelectable)
19370 return -1;
19371 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19373 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19375 double posKey, posValue;
19376 pixelsToCoords(pos, posKey, posValue);
19377 // quartile box:
19378 QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19379 QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19380 if (keyRange.contains(posKey) && valueRange.contains(posValue))
19381 return mParentPlot->selectionTolerance()*0.99;
19383 // min/max whiskers:
19384 if (QCPRange(mMinimum, mMaximum).contains(posValue))
19385 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
19387 return -1;
19390 /* inherits documentation from base class */
19391 void QCPStatisticalBox::draw(QCPPainter *painter)
19393 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19395 // check data validity if flag set:
19396 #ifdef QCUSTOMPLOT_CHECK_DATA
19397 if (QCP::isInvalidData(mKey, mMedian) ||
19398 QCP::isInvalidData(mLowerQuartile, mUpperQuartile) ||
19399 QCP::isInvalidData(mMinimum, mMaximum))
19400 qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19401 for (int i=0; i<mOutliers.size(); ++i)
19402 if (QCP::isInvalidData(mOutliers.at(i)))
19403 qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19404 #endif
19406 QRectF quartileBox;
19407 drawQuartileBox(painter, &quartileBox);
19409 painter->save();
19410 painter->setClipRect(quartileBox, Qt::IntersectClip);
19411 drawMedian(painter);
19412 painter->restore();
19414 drawWhiskers(painter);
19415 drawOutliers(painter);
19418 /* inherits documentation from base class */
19419 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19421 // draw filled rect:
19422 applyDefaultAntialiasingHint(painter);
19423 painter->setPen(mPen);
19424 painter->setBrush(mBrush);
19425 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19426 r.moveCenter(rect.center());
19427 painter->drawRect(r);
19430 /*! \internal
19432 Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
19433 coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
19434 the median doesn't draw outside the quartile box).
19436 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19438 QRectF box;
19439 box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19440 box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19441 applyDefaultAntialiasingHint(painter);
19442 painter->setPen(mainPen());
19443 painter->setBrush(mainBrush());
19444 painter->drawRect(box);
19445 if (quartileBox)
19446 *quartileBox = box;
19449 /*! \internal
19451 Draws the median line inside the quartile box.
19453 void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
19455 QLineF medianLine;
19456 medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19457 medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19458 applyDefaultAntialiasingHint(painter);
19459 painter->setPen(mMedianPen);
19460 painter->drawLine(medianLine);
19463 /*! \internal
19465 Draws both whisker backbones and bars.
19467 void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
19469 QLineF backboneMin, backboneMax, barMin, barMax;
19470 backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19471 backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19472 barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
19473 barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
19474 applyErrorBarsAntialiasingHint(painter);
19475 painter->setPen(mWhiskerPen);
19476 painter->drawLine(backboneMin);
19477 painter->drawLine(backboneMax);
19478 painter->setPen(mWhiskerBarPen);
19479 painter->drawLine(barMin);
19480 painter->drawLine(barMax);
19483 /*! \internal
19485 Draws the outlier scatter points.
19487 void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
19489 applyScattersAntialiasingHint(painter);
19490 mOutlierStyle.applyTo(painter, mPen);
19491 for (int i=0; i<mOutliers.size(); ++i)
19492 mOutlierStyle.drawShape(painter, coordsToPixels(mKey, mOutliers.at(i)));
19495 /* inherits documentation from base class */
19496 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19498 foundRange = true;
19499 if (inSignDomain == sdBoth)
19501 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19502 } else if (inSignDomain == sdNegative)
19504 if (mKey+mWidth*0.5 < 0)
19505 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19506 else if (mKey < 0)
19507 return QCPRange(mKey-mWidth*0.5, mKey);
19508 else
19510 foundRange = false;
19511 return QCPRange();
19513 } else if (inSignDomain == sdPositive)
19515 if (mKey-mWidth*0.5 > 0)
19516 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19517 else if (mKey > 0)
19518 return QCPRange(mKey, mKey+mWidth*0.5);
19519 else
19521 foundRange = false;
19522 return QCPRange();
19525 foundRange = false;
19526 return QCPRange();
19529 /* inherits documentation from base class */
19530 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19532 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
19533 values.reserve(mOutliers.size() + 5);
19534 values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
19535 values << mOutliers;
19536 // go through values and find the ones in legal range:
19537 bool haveUpper = false;
19538 bool haveLower = false;
19539 double upper = 0;
19540 double lower = 0;
19541 for (int i=0; i<values.size(); ++i)
19543 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
19544 (inSignDomain == sdPositive && values.at(i) > 0) ||
19545 (inSignDomain == sdBoth))
19547 if (values.at(i) > upper || !haveUpper)
19549 upper = values.at(i);
19550 haveUpper = true;
19552 if (values.at(i) < lower || !haveLower)
19554 lower = values.at(i);
19555 haveLower = true;
19559 // return the bounds if we found some sensible values:
19560 if (haveLower && haveUpper)
19562 foundRange = true;
19563 return QCPRange(lower, upper);
19564 } else // might happen if all values are in other sign domain
19566 foundRange = false;
19567 return QCPRange();
19572 ////////////////////////////////////////////////////////////////////////////////////////////////////
19573 //////////////////// QCPColorMapData
19574 ////////////////////////////////////////////////////////////////////////////////////////////////////
19576 /*! \class QCPColorMapData
19577 \brief Holds the two-dimensional data of a QCPColorMap plottable.
19579 This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref
19580 QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a
19581 color, depending on the value.
19583 The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize).
19584 Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref
19585 setKeyRange, \ref setValueRange).
19587 The data cells can be accessed in two ways: They can be directly addressed by an integer index
19588 with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot
19589 coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are
19590 provided by the functions \ref coordToCell and \ref cellToCoord.
19592 This class also buffers the minimum and maximum values that are in the data set, to provide
19593 QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value
19594 that is greater than the current maximum increases this maximum to the new value. However,
19595 setting the cell that currently holds the maximum value to a smaller value doesn't decrease the
19596 maximum again, because finding the true new maximum would require going through the entire data
19597 array, which might be time consuming. The same holds for the data minimum. This functionality is
19598 given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the
19599 true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience
19600 parameter \a recalculateDataBounds which may be set to true to automatically call \ref
19601 recalculateDataBounds internally.
19604 /* start of documentation of inline functions */
19606 /*! \fn bool QCPColorMapData::isEmpty() const
19608 Returns whether this instance carries no data. This is equivalent to having a size where at least
19609 one of the dimensions is 0 (see \ref setSize).
19612 /* end of documentation of inline functions */
19615 Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction
19616 and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap
19617 at the coordinates \a keyRange and \a valueRange.
19619 \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
19621 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
19622 mKeySize(0),
19623 mValueSize(0),
19624 mKeyRange(keyRange),
19625 mValueRange(valueRange),
19626 mIsEmpty(true),
19627 mData(0),
19628 mDataModified(true)
19630 setSize(keySize, valueSize);
19631 fill(0);
19634 QCPColorMapData::~QCPColorMapData()
19636 if (mData)
19637 delete[] mData;
19641 Constructs a new QCPColorMapData instance copying the data and range of \a other.
19643 QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
19644 mKeySize(0),
19645 mValueSize(0),
19646 mIsEmpty(true),
19647 mData(0),
19648 mDataModified(true)
19650 *this = other;
19654 Overwrites this color map data instance with the data stored in \a other.
19656 QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
19658 if (&other != this)
19660 const int keySize = other.keySize();
19661 const int valueSize = other.valueSize();
19662 setSize(keySize, valueSize);
19663 setRange(other.keyRange(), other.valueRange());
19664 if (!mIsEmpty)
19665 memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
19666 mDataBounds = other.mDataBounds;
19667 mDataModified = true;
19669 return *this;
19672 /* undocumented getter */
19673 double QCPColorMapData::data(double key, double value)
19675 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19676 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19677 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19678 return mData[valueCell*mKeySize + keyCell];
19679 else
19680 return 0;
19683 /* undocumented getter */
19684 double QCPColorMapData::cell(int keyIndex, int valueIndex)
19686 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19687 return mData[valueIndex*mKeySize + keyIndex];
19688 else
19689 return 0;
19693 Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in
19694 the value dimension.
19696 The current data is discarded and the map cells are set to 0, unless the map had already the
19697 requested size.
19699 Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref
19700 isEmpty returns true.
19702 \see setRange, setKeySize, setValueSize
19704 void QCPColorMapData::setSize(int keySize, int valueSize)
19706 if (keySize != mKeySize || valueSize != mValueSize)
19708 mKeySize = keySize;
19709 mValueSize = valueSize;
19710 if (mData)
19711 delete[] mData;
19712 mIsEmpty = mKeySize == 0 || mValueSize == 0;
19713 if (!mIsEmpty)
19715 #ifdef __EXCEPTIONS
19716 try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
19717 #endif
19718 mData = new double[mKeySize*mValueSize];
19719 #ifdef __EXCEPTIONS
19720 } catch (...) { mData = 0; }
19721 #endif
19722 if (mData)
19723 fill(0);
19724 else
19725 qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
19726 } else
19727 mData = 0;
19728 mDataModified = true;
19733 Resizes the data array to have \a keySize cells in the key dimension.
19735 The current data is discarded and the map cells are set to 0, unless the map had already the
19736 requested size.
19738 Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true.
19740 \see setKeyRange, setSize, setValueSize
19742 void QCPColorMapData::setKeySize(int keySize)
19744 setSize(keySize, mValueSize);
19748 Resizes the data array to have \a valueSize cells in the value dimension.
19750 The current data is discarded and the map cells are set to 0, unless the map had already the
19751 requested size.
19753 Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true.
19755 \see setValueRange, setSize, setKeySize
19757 void QCPColorMapData::setValueSize(int valueSize)
19759 setSize(mKeySize, valueSize);
19763 Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area
19764 covered by the color map in plot coordinates.
19766 The outer cells will be centered on the range boundaries given to this function. For example, if
19767 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19768 be cells centered on the key coordinates 2, 2.5 and 3.
19770 \see setSize
19772 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
19774 setKeyRange(keyRange);
19775 setValueRange(valueRange);
19779 Sets the coordinate range the data shall be distributed over in the key dimension. Together with
19780 the value range, This defines the rectangular area covered by the color map in plot coordinates.
19782 The outer cells will be centered on the range boundaries given to this function. For example, if
19783 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19784 be cells centered on the key coordinates 2, 2.5 and 3.
19786 \see setRange, setValueRange, setSize
19788 void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
19790 mKeyRange = keyRange;
19794 Sets the coordinate range the data shall be distributed over in the value dimension. Together with
19795 the key range, This defines the rectangular area covered by the color map in plot coordinates.
19797 The outer cells will be centered on the range boundaries given to this function. For example, if
19798 the value size (\ref setValueSize) is 3 and \a valueRange is set to <tt>QCPRange(2, 3)</tt> there
19799 will be cells centered on the value coordinates 2, 2.5 and 3.
19801 \see setRange, setKeyRange, setSize
19803 void QCPColorMapData::setValueRange(const QCPRange &valueRange)
19805 mValueRange = valueRange;
19809 Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a
19812 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19813 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19814 you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
19815 determine the cell index. Rather directly access the cell index with \ref
19816 QCPColorMapData::setCell.
19818 \see setCell, setRange
19820 void QCPColorMapData::setData(double key, double value, double z)
19822 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19823 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19824 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19826 mData[valueCell*mKeySize + keyCell] = z;
19827 if (z < mDataBounds.lower)
19828 mDataBounds.lower = z;
19829 if (z > mDataBounds.upper)
19830 mDataBounds.upper = z;
19831 mDataModified = true;
19836 Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices
19837 enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see
19838 \ref setSize).
19840 In the standard plot configuration (horizontal key axis and vertical value axis, both not
19841 range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with
19842 indices (keySize-1, valueSize-1) is in the top right corner of the color map.
19844 \see setData, setSize
19846 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
19848 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19850 mData[valueIndex*mKeySize + keyIndex] = z;
19851 if (z < mDataBounds.lower)
19852 mDataBounds.lower = z;
19853 if (z > mDataBounds.upper)
19854 mDataBounds.upper = z;
19855 mDataModified = true;
19860 Goes through the data and updates the buffered minimum and maximum data values.
19862 Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange
19863 and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten
19864 with a smaller or larger value respectively, since the buffered maximum/minimum values have been
19865 updated the last time. Why this is the case is explained in the class description (\ref
19866 QCPColorMapData).
19868 Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a
19869 recalculateDataBounds for convenience. Setting this to true will call this method for you, before
19870 doing the rescale.
19872 void QCPColorMapData::recalculateDataBounds()
19874 if (mKeySize > 0 && mValueSize > 0)
19876 double minHeight = mData[0];
19877 double maxHeight = mData[0];
19878 const int dataCount = mValueSize*mKeySize;
19879 for (int i=0; i<dataCount; ++i)
19881 if (mData[i] > maxHeight)
19882 maxHeight = mData[i];
19883 if (mData[i] < minHeight)
19884 minHeight = mData[i];
19886 mDataBounds.lower = minHeight;
19887 mDataBounds.upper = maxHeight;
19892 Frees the internal data memory.
19894 This is equivalent to calling \ref setSize "setSize(0, 0)".
19896 void QCPColorMapData::clear()
19898 setSize(0, 0);
19902 Sets all cells to the value \a z.
19904 void QCPColorMapData::fill(double z)
19906 const int dataCount = mValueSize*mKeySize;
19907 for (int i=0; i<dataCount; ++i)
19908 mData[i] = z;
19909 mDataBounds = QCPRange(z, z);
19910 mDataModified = true;
19914 Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData
19915 instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a
19916 valueIndex.
19918 The retrieved key/value cell indices can then be used for example with \ref setCell.
19920 If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
19921 keyIndex.
19923 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19924 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19925 you shouldn't use the \ref QCPColorMapData::coordToCell method as it uses a linear transformation to
19926 determine the cell index.
19928 \see cellToCoord, QCPAxis::coordToPixel
19930 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
19932 if (keyIndex)
19933 *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19934 if (valueIndex)
19935 *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19939 Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData
19940 instance. The resulting coordinates are returned via the output parameters \a key and \a
19941 value.
19943 If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
19944 value.
19946 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
19947 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
19948 you shouldn't use the \ref QCPColorMapData::cellToCoord method as it uses a linear transformation to
19949 determine the cell index.
19951 \see coordToCell, QCPAxis::pixelToCoord
19953 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
19955 if (key)
19956 *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
19957 if (value)
19958 *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
19962 ////////////////////////////////////////////////////////////////////////////////////////////////////
19963 //////////////////// QCPColorMap
19964 ////////////////////////////////////////////////////////////////////////////////////////////////////
19966 /*! \class QCPColorMap
19967 \brief A plottable representing a two-dimensional color map in a plot.
19969 \image html QCPColorMap.png
19971 The data is stored in the class \ref QCPColorMapData, which can be accessed via the data()
19972 method.
19974 A color map has three dimensions to represent a data point: The \a key dimension, the \a value
19975 dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value
19976 correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QColorMap
19977 constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a
19978 value).
19980 Set the number of points (or \a cells) in the key/value dimension via \ref
19981 QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is
19982 specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range
19983 boundary and the last cell will be centered on the upper range boundary. The data can be set by
19984 either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via
19985 their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer
19986 setCell, since it doesn't need to do any coordinate transformation and thus performs a bit
19987 better.
19989 The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)
19990 key and value axes.
19992 To show the user which colors correspond to which \a data values, a \ref QCPColorScale is
19993 typically placed to the right of the axis rect. See the documentation there for details on how to
19994 add and use a color scale.
19996 \section appearance Changing the appearance
19998 The central part of the appearance is the color gradient, which can be specified via \ref
19999 setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
20000 gradient.
20002 The \a data range that is mapped to the colors of the gradient can be specified with \ref
20003 setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
20004 rescaleDataRange.
20006 \section usage Usage
20008 Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable
20009 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
20010 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
20012 Usually, you first create an instance and add it to the customPlot:
20013 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1
20014 and then modify the properties of the newly created color map, e.g.:
20015 \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2
20017 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
20018 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
20019 you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
20020 determine the cell index. Rather directly access the cell index with \ref
20021 QCPColorMapData::setCell.
20024 /* start documentation of inline functions */
20026 /*! \fn QCPColorMapData *QCPColorMap::data() const
20028 Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to
20029 modify data points (cells) and the color map key/value range.
20031 \see setData
20034 /* end documentation of inline functions */
20036 /* start documentation of signals */
20038 /*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
20040 This signal is emitted when the data range changes.
20042 \see setDataRange
20045 /*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
20047 This signal is emitted when the data scale type changes.
20049 \see setDataScaleType
20052 /*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
20054 This signal is emitted when the gradient changes.
20056 \see setGradient
20059 /* end documentation of signals */
20062 Constructs a color map with the specified \a keyAxis and \a valueAxis.
20064 The constructed QCPColorMap can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20065 then takes ownership of the color map.
20067 QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20068 QCPAbstractPlottable(keyAxis, valueAxis),
20069 mDataScaleType(QCPAxis::stLinear),
20070 mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20071 mInterpolate(true),
20072 mTightBoundary(false),
20073 mMapImageInvalidated(true)
20077 QCPColorMap::~QCPColorMap()
20079 delete mMapData;
20083 Replaces the current \ref data with the provided \a data.
20085 If \a copy is set to true, the \a data object will only be copied. if false, the color map
20086 takes ownership of the passed data and replaces the internal data pointer with it. This is
20087 significantly faster than copying for large datasets.
20089 void QCPColorMap::setData(QCPColorMapData *data, bool copy)
20091 if (mMapData == data)
20093 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20094 return;
20096 if (copy)
20098 *mMapData = *data;
20099 } else
20101 delete mMapData;
20102 mMapData = data;
20104 mMapImageInvalidated = true;
20108 Sets the data range of this color map to \a dataRange. The data range defines which data values
20109 are mapped to the color gradient.
20111 To make the data range span the full range of the data set, use \ref rescaleDataRange.
20113 \see QCPColorScale::setDataRange
20115 void QCPColorMap::setDataRange(const QCPRange &dataRange)
20117 if (!QCPRange::validRange(dataRange)) return;
20118 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20120 if (mDataScaleType == QCPAxis::stLogarithmic)
20121 mDataRange = dataRange.sanitizedForLogScale();
20122 else
20123 mDataRange = dataRange.sanitizedForLinScale();
20124 mMapImageInvalidated = true;
20125 emit dataRangeChanged(mDataRange);
20130 Sets whether the data is correlated with the color gradient linearly or logarithmically.
20132 \see QCPColorScale::setDataScaleType
20134 void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
20136 if (mDataScaleType != scaleType)
20138 mDataScaleType = scaleType;
20139 mMapImageInvalidated = true;
20140 emit dataScaleTypeChanged(mDataScaleType);
20141 if (mDataScaleType == QCPAxis::stLogarithmic)
20142 setDataRange(mDataRange.sanitizedForLogScale());
20147 Sets the color gradient that is used to represent the data. For more details on how to create an
20148 own gradient or use one of the preset gradients, see \ref QCPColorGradient.
20150 The colors defined by the gradient will be used to represent data values in the currently set
20151 data range, see \ref setDataRange. Data points that are outside this data range will either be
20152 colored uniformly with the respective gradient boundary color, or the gradient will repeat,
20153 depending on \ref QCPColorGradient::setPeriodic.
20155 \see QCPColorScale::setGradient
20157 void QCPColorMap::setGradient(const QCPColorGradient &gradient)
20159 if (mGradient != gradient)
20161 mGradient = gradient;
20162 mMapImageInvalidated = true;
20163 emit gradientChanged(mGradient);
20168 Sets whether the color map image shall use bicubic interpolation when displaying the color map
20169 shrinked or expanded, and not at a 1:1 pixel-to-data scale.
20171 \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled"
20173 void QCPColorMap::setInterpolate(bool enabled)
20175 mInterpolate = enabled;
20176 mMapImageInvalidated = true; // because oversampling factors might need to change
20180 Sets whether the outer most data rows and columns are clipped to the specified key and value
20181 range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange).
20183 if \a enabled is set to false, the data points at the border of the color map are drawn with the
20184 same width and height as all other data points. Since the data points are represented by
20185 rectangles of one color centered on the data coordinate, this means that the shown color map
20186 extends by half a data point over the specified key/value range in each direction.
20188 \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled"
20190 void QCPColorMap::setTightBoundary(bool enabled)
20192 mTightBoundary = enabled;
20196 Associates the color scale \a colorScale with this color map.
20198 This means that both the color scale and the color map synchronize their gradient, data range and
20199 data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps
20200 can be associated with one single color scale. This causes the color maps to also synchronize
20201 those properties, via the mutual color scale.
20203 This function causes the color map to adopt the current color gradient, data range and data scale
20204 type of \a colorScale. After this call, you may change these properties at either the color map
20205 or the color scale, and the setting will be applied to both.
20207 Pass 0 as \a colorScale to disconnect the color scale from this color map again.
20209 void QCPColorMap::setColorScale(QCPColorScale *colorScale)
20211 if (mColorScale) // unconnect signals from old color scale
20213 disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20214 disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20215 disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20216 disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20217 disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20218 disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20220 mColorScale = colorScale;
20221 if (mColorScale) // connect signals to new color scale
20223 setGradient(mColorScale.data()->gradient());
20224 setDataRange(mColorScale.data()->dataRange());
20225 setDataScaleType(mColorScale.data()->dataScaleType());
20226 connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20227 connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20228 connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20229 connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20230 connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20231 connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20236 Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the
20237 current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods,
20238 only for the third data dimension of the color map.
20240 The minimum and maximum values of the data set are buffered in the internal QCPColorMapData
20241 instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref
20242 QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For
20243 performance reasons, however, they are only updated in an expanding fashion. So the buffered
20244 maximum can only increase and the buffered minimum can only decrease. In consequence, changes to
20245 the data that actually lower the maximum of the data set (by overwriting the cell holding the
20246 current maximum with a smaller value), aren't recognized and the buffered maximum overestimates
20247 the true maximum of the data set. The same happens for the buffered minimum. To recalculate the
20248 true minimum and maximum by explicitly looking at each cell, the method
20249 QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a
20250 recalculateDataBounds calls this method before setting the data range to the buffered minimum and
20251 maximum.
20253 \see setDataRange
20255 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20257 if (recalculateDataBounds)
20258 mMapData->recalculateDataBounds();
20259 setDataRange(mMapData->dataBounds());
20263 Takes the current appearance of the color map and updates the legend icon, which is used to
20264 represent this color map in the legend (see \ref QCPLegend).
20266 The \a transformMode specifies whether the rescaling is done by a faster, low quality image
20267 scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm
20268 (Qt::SmoothTransformation).
20270 The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to
20271 the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured
20272 legend icon size, the thumb will be rescaled during drawing of the legend item.
20274 \see setDataRange
20276 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20278 if (mMapImage.isNull() && !data()->isEmpty())
20279 updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20281 if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20283 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20284 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20285 mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20290 Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also
20291 resizes the map to 0x0 cells.
20293 void QCPColorMap::clearData()
20295 mMapData->clear();
20298 /* inherits documentation from base class */
20299 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20301 Q_UNUSED(details)
20302 if (onlySelectable && !mSelectable)
20303 return -1;
20304 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20306 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20308 double posKey, posValue;
20309 pixelsToCoords(pos, posKey, posValue);
20310 if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20311 return mParentPlot->selectionTolerance()*0.99;
20313 return -1;
20316 /*! \internal
20318 Updates the internal map image buffer by going through the internal \ref QCPColorMapData and
20319 turning the data values into color pixels with \ref QCPColorGradient::colorize.
20321 This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image
20322 has been invalidated for a different reason (e.g. a change of the data range with \ref
20323 setDataRange).
20325 If the map cell count is low, the image created will be oversampled in order to avoid a
20326 QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images
20327 without smooth transform enabled. Accordingly, oversampling isn't performed if \ref
20328 setInterpolate is true.
20330 void QCPColorMap::updateMapImage()
20332 QCPAxis *keyAxis = mKeyAxis.data();
20333 if (!keyAxis) return;
20334 if (mMapData->isEmpty()) return;
20336 const int keySize = mMapData->keySize();
20337 const int valueSize = mMapData->valueSize();
20338 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
20339 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
20341 // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
20342 if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
20343 mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32);
20344 else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
20345 mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32);
20347 QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
20348 if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20350 // resize undersampled map image to actual key/value cell sizes:
20351 if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
20352 mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
20353 else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
20354 mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
20355 localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
20356 } else if (!mUndersampledMapImage.isNull())
20357 mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
20359 const double *rawData = mMapData->mData;
20360 if (keyAxis->orientation() == Qt::Horizontal)
20362 const int lineCount = valueSize;
20363 const int rowCount = keySize;
20364 for (int line=0; line<lineCount; ++line)
20366 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)
20367 mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20369 } else // keyAxis->orientation() == Qt::Vertical
20371 const int lineCount = keySize;
20372 const int rowCount = valueSize;
20373 for (int line=0; line<lineCount; ++line)
20375 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)
20376 mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20380 if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
20382 if (keyAxis->orientation() == Qt::Horizontal)
20383 mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20384 else
20385 mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
20387 mMapData->mDataModified = false;
20388 mMapImageInvalidated = false;
20391 /* inherits documentation from base class */
20392 void QCPColorMap::draw(QCPPainter *painter)
20394 if (mMapData->isEmpty()) return;
20395 if (!mKeyAxis || !mValueAxis) return;
20396 applyDefaultAntialiasingHint(painter);
20398 if (mMapData->mDataModified || mMapImageInvalidated)
20399 updateMapImage();
20401 // use buffer if painting vectorized (PDF):
20402 bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
20403 QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
20404 QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
20405 QPixmap mapBuffer;
20406 double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
20407 if (useBuffer)
20409 mapBufferTarget = painter->clipRegion().boundingRect();
20410 mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
20411 mapBuffer.fill(Qt::transparent);
20412 localPainter = new QCPPainter(&mapBuffer);
20413 localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
20414 localPainter->translate(-mapBufferTarget.topLeft());
20417 QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20418 coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
20419 // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
20420 double halfCellWidth = 0; // in pixels
20421 double halfCellHeight = 0; // in pixels
20422 if (keyAxis()->orientation() == Qt::Horizontal)
20424 if (mMapData->keySize() > 1)
20425 halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
20426 if (mMapData->valueSize() > 1)
20427 halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
20428 } else // keyAxis orientation is Qt::Vertical
20430 if (mMapData->keySize() > 1)
20431 halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
20432 if (mMapData->valueSize() > 1)
20433 halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
20435 imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
20436 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20437 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20438 bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20439 localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20440 QRegion clipBackup;
20441 if (mTightBoundary)
20443 clipBackup = localPainter->clipRegion();
20444 QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20445 coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
20446 localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
20448 localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20449 if (mTightBoundary)
20450 localPainter->setClipRegion(clipBackup);
20451 localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20453 if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
20455 delete localPainter;
20456 painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
20460 /* inherits documentation from base class */
20461 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20463 applyDefaultAntialiasingHint(painter);
20464 // draw map thumbnail:
20465 if (!mLegendIcon.isNull())
20467 QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20468 QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20469 iconRect.moveCenter(rect.center());
20470 painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20473 // draw frame:
20474 painter->setBrush(Qt::NoBrush);
20475 painter->setPen(Qt::black);
20476 painter->drawRect(rect.adjusted(1, 1, 0, 0));
20480 /* inherits documentation from base class */
20481 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20483 foundRange = true;
20484 QCPRange result = mMapData->keyRange();
20485 result.normalize();
20486 if (inSignDomain == QCPAbstractPlottable::sdPositive)
20488 if (result.lower <= 0 && result.upper > 0)
20489 result.lower = result.upper*1e-3;
20490 else if (result.lower <= 0 && result.upper <= 0)
20491 foundRange = false;
20492 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20494 if (result.upper >= 0 && result.lower < 0)
20495 result.upper = result.lower*1e-3;
20496 else if (result.upper >= 0 && result.lower >= 0)
20497 foundRange = false;
20499 return result;
20502 /* inherits documentation from base class */
20503 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20505 foundRange = true;
20506 QCPRange result = mMapData->valueRange();
20507 result.normalize();
20508 if (inSignDomain == QCPAbstractPlottable::sdPositive)
20510 if (result.lower <= 0 && result.upper > 0)
20511 result.lower = result.upper*1e-3;
20512 else if (result.lower <= 0 && result.upper <= 0)
20513 foundRange = false;
20514 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20516 if (result.upper >= 0 && result.lower < 0)
20517 result.upper = result.lower*1e-3;
20518 else if (result.upper >= 0 && result.lower >= 0)
20519 foundRange = false;
20521 return result;
20525 ////////////////////////////////////////////////////////////////////////////////////////////////////
20526 //////////////////// QCPFinancialData
20527 ////////////////////////////////////////////////////////////////////////////////////////////////////
20529 /*! \class QCPFinancialData
20530 \brief Holds the data of one single data point for QCPFinancial.
20532 The container for storing multiple data points is \ref QCPFinancialDataMap.
20534 The stored data is:
20535 \li \a key: coordinate on the key axis of this data point
20536 \li \a open: The opening value at the data point
20537 \li \a high: The high/maximum value at the data point
20538 \li \a low: The low/minimum value at the data point
20539 \li \a close: The closing value at the data point
20541 \see QCPFinancialDataMap
20545 Constructs a data point with key and all values set to zero.
20547 QCPFinancialData::QCPFinancialData() :
20548 key(0),
20549 open(0),
20550 high(0),
20551 low(0),
20552 close(0)
20557 Constructs a data point with the specified \a key and OHLC values.
20559 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20560 key(key),
20561 open(open),
20562 high(high),
20563 low(low),
20564 close(close)
20569 ////////////////////////////////////////////////////////////////////////////////////////////////////
20570 //////////////////// QCPFinancial
20571 ////////////////////////////////////////////////////////////////////////////////////////////////////
20573 /*! \class QCPFinancial
20574 \brief A plottable representing a financial stock chart
20576 \image html QCPFinancial.png
20578 This plottable represents time series data binned to certain intervals, mainly used for stock
20579 charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be
20580 set via \ref setChartStyle.
20582 The data is passed via \ref setData as a set of open/high/low/close values at certain keys
20583 (typically times). This means the data must be already binned appropriately. If data is only
20584 available as a series of values (e.g. \a price against \a time), you can use the static
20585 convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed
20586 to \ref setData.
20588 The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and is given in plot
20589 key coordinates. A typical choice is to set it to (or slightly less than) one bin interval width.
20591 \section appearance Changing the appearance
20593 Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored,
20594 lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush).
20596 If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are
20597 represented with a different pen and brush than negative changes (\a close < \a open). These can
20598 be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref
20599 setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection
20600 however, the normal selected pen/brush (\ref setSelectedPen, \ref setSelectedBrush) is used,
20601 irrespective of whether the chart is single- or two-colored.
20605 /* start of documentation of inline functions */
20607 /*! \fn QCPFinancialDataMap *QCPFinancial::data() const
20609 Returns a pointer to the internal data storage of type \ref QCPFinancialDataMap. You may use it to
20610 directly manipulate the data, which may be more convenient and faster than using the regular \ref
20611 setData or \ref addData methods, in certain situations.
20614 /* end of documentation of inline functions */
20617 Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
20618 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
20619 the same orientation. If either of these restrictions is violated, a corresponding message is
20620 printed to the debug output (qDebug), the construction is not aborted, though.
20622 The constructed QCPFinancial can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20623 then takes ownership of the financial chart.
20625 QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20626 QCPAbstractPlottable(keyAxis, valueAxis),
20627 mData(0),
20628 mChartStyle(csOhlc),
20629 mWidth(0.5),
20630 mTwoColored(false),
20631 mBrushPositive(QBrush(QColor(210, 210, 255))),
20632 mBrushNegative(QBrush(QColor(255, 210, 210))),
20633 mPenPositive(QPen(QColor(10, 40, 180))),
20634 mPenNegative(QPen(QColor(180, 40, 10)))
20636 mData = new QCPFinancialDataMap;
20638 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
20639 setSelectedBrush(QBrush(QColor(80, 80, 255)));
20642 QCPFinancial::~QCPFinancial()
20644 delete mData;
20648 Replaces the current data with the provided \a data.
20650 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
20651 takes ownership of the passed data and replaces the internal data pointer with it. This is
20652 significantly faster than copying for large datasets.
20654 Alternatively, you can also access and modify the plottable's data via the \ref data method, which
20655 returns a pointer to the internal \ref QCPFinancialDataMap.
20657 \see timeSeriesToOhlc
20659 void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy)
20661 if (mData == data)
20663 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20664 return;
20666 if (copy)
20668 *mData = *data;
20669 } else
20671 delete mData;
20672 mData = data;
20676 /*! \overload
20678 Replaces the current data with the provided open/high/low/close data. The provided vectors should
20679 have equal length. Else, the number of added points will be the size of the smallest vector.
20681 \see timeSeriesToOhlc
20683 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20685 mData->clear();
20686 int n = key.size();
20687 n = qMin(n, open.size());
20688 n = qMin(n, high.size());
20689 n = qMin(n, low.size());
20690 n = qMin(n, close.size());
20691 for (int i=0; i<n; ++i)
20693 mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20698 Sets which representation style shall be used to display the OHLC data.
20700 void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)
20702 mChartStyle = style;
20706 Sets the width of the individual bars/candlesticks to \a width in plot key coordinates.
20708 A typical choice is to set it to (or slightly less than) one bin interval width.
20710 void QCPFinancial::setWidth(double width)
20712 mWidth = width;
20716 Sets whether this chart shall contrast positive from negative trends per data point by using two
20717 separate colors to draw the respective bars/candlesticks.
20719 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20720 setBrush).
20722 \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative
20724 void QCPFinancial::setTwoColored(bool twoColored)
20726 mTwoColored = twoColored;
20730 If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20731 of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20733 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20734 setBrush).
20736 \see setBrushNegative, setPenPositive, setPenNegative
20738 void QCPFinancial::setBrushPositive(const QBrush &brush)
20740 mBrushPositive = brush;
20744 If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20745 of data points with a negative trend (i.e. bars/candlesticks with close < open).
20747 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20748 setBrush).
20750 \see setBrushPositive, setPenNegative, setPenPositive
20752 void QCPFinancial::setBrushNegative(const QBrush &brush)
20754 mBrushNegative = brush;
20758 If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20759 outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20761 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20762 setBrush).
20764 \see setPenNegative, setBrushPositive, setBrushNegative
20766 void QCPFinancial::setPenPositive(const QPen &pen)
20768 mPenPositive = pen;
20772 If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20773 outlines of data points with a negative trend (i.e. bars/candlesticks with close < open).
20775 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20776 setBrush).
20778 \see setPenPositive, setBrushNegative, setBrushPositive
20780 void QCPFinancial::setPenNegative(const QPen &pen)
20782 mPenNegative = pen;
20786 Adds the provided data points in \a dataMap to the current data.
20788 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20789 pointer to the internal \ref QCPFinancialDataMap.
20791 \see removeData
20793 void QCPFinancial::addData(const QCPFinancialDataMap &dataMap)
20795 mData->unite(dataMap);
20798 /*! \overload
20800 Adds the provided single data point in \a data to the current data.
20802 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20803 pointer to the internal \ref QCPFinancialData.
20805 \see removeData
20807 void QCPFinancial::addData(const QCPFinancialData &data)
20809 mData->insertMulti(data.key, data);
20812 /*! \overload
20814 Adds the provided single data point given by \a key, \a open, \a high, \a low, and \a close to
20815 the current data.
20817 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20818 pointer to the internal \ref QCPFinancialData.
20820 \see removeData
20822 void QCPFinancial::addData(double key, double open, double high, double low, double close)
20824 mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
20827 /*! \overload
20829 Adds the provided open/high/low/close data to the current data.
20831 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20832 pointer to the internal \ref QCPFinancialData.
20834 \see removeData
20836 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20838 int n = key.size();
20839 n = qMin(n, open.size());
20840 n = qMin(n, high.size());
20841 n = qMin(n, low.size());
20842 n = qMin(n, close.size());
20843 for (int i=0; i<n; ++i)
20845 mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20850 Removes all data points with keys smaller than \a key.
20852 \see addData, clearData
20854 void QCPFinancial::removeDataBefore(double key)
20856 QCPFinancialDataMap::iterator it = mData->begin();
20857 while (it != mData->end() && it.key() < key)
20858 it = mData->erase(it);
20862 Removes all data points with keys greater than \a key.
20864 \see addData, clearData
20866 void QCPFinancial::removeDataAfter(double key)
20868 if (mData->isEmpty()) return;
20869 QCPFinancialDataMap::iterator it = mData->upperBound(key);
20870 while (it != mData->end())
20871 it = mData->erase(it);
20875 Removes all data points with keys between \a fromKey and \a toKey. if \a fromKey is greater or
20876 equal to \a toKey, the function does nothing. To remove a single data point with known key, use
20877 \ref removeData(double key).
20879 \see addData, clearData
20881 void QCPFinancial::removeData(double fromKey, double toKey)
20883 if (fromKey >= toKey || mData->isEmpty()) return;
20884 QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
20885 QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
20886 while (it != itEnd)
20887 it = mData->erase(it);
20890 /*! \overload
20892 Removes a single data point at \a key. If the position is not known with absolute precision,
20893 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
20894 around the suspected position, depeding on the precision with which the key is known.
20896 \see addData, clearData
20898 void QCPFinancial::removeData(double key)
20900 mData->remove(key);
20904 Removes all data points.
20906 \see removeData, removeDataAfter, removeDataBefore
20908 void QCPFinancial::clearData()
20910 mData->clear();
20913 /* inherits documentation from base class */
20914 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20916 Q_UNUSED(details)
20917 if (onlySelectable && !mSelectable)
20918 return -1;
20919 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20921 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20923 // get visible data range:
20924 QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20925 getVisibleDataBounds(lower, upper);
20926 if (lower == mData->constEnd() || upper == mData->constEnd())
20927 return -1;
20928 // perform select test according to configured style:
20929 switch (mChartStyle)
20931 case QCPFinancial::csOhlc:
20932 return ohlcSelectTest(pos, lower, upper+1); break;
20933 case QCPFinancial::csCandlestick:
20934 return candlestickSelectTest(pos, lower, upper+1); break;
20937 return -1;
20941 A convenience function that converts time series data (\a value against \a time) to OHLC binned
20942 data points. The return value can then be passed on to \ref setData.
20944 The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given.
20945 For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour
20946 each, set \a timeBinSize to 3600.
20948 \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The
20949 value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys.
20950 It merely defines the mathematical offset/phase of the bins that will be used to process the
20951 data.
20953 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
20955 QCPFinancialDataMap map;
20956 int count = qMin(time.size(), value.size());
20957 if (count == 0)
20958 return QCPFinancialDataMap();
20960 QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
20961 int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
20962 for (int i=0; i<count; ++i)
20964 int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
20965 if (currentBinIndex == index) // data point still in current bin, extend high/low:
20967 if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
20968 if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
20969 if (i == count-1) // last data point is in current bin, finalize bin:
20971 currentBinData.close = value.at(i);
20972 currentBinData.key = timeBinOffset+(index)*timeBinSize;
20973 map.insert(currentBinData.key, currentBinData);
20975 } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
20977 // finalize current bin:
20978 currentBinData.close = value.at(i-1);
20979 currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
20980 map.insert(currentBinData.key, currentBinData);
20981 // start next bin:
20982 currentBinIndex = index;
20983 currentBinData.open = value.at(i);
20984 currentBinData.high = value.at(i);
20985 currentBinData.low = value.at(i);
20989 return map;
20992 /* inherits documentation from base class */
20993 void QCPFinancial::draw(QCPPainter *painter)
20995 // get visible data range:
20996 QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20997 getVisibleDataBounds(lower, upper);
20998 if (lower == mData->constEnd() || upper == mData->constEnd())
20999 return;
21001 // draw visible data range according to configured style:
21002 switch (mChartStyle)
21004 case QCPFinancial::csOhlc:
21005 drawOhlcPlot(painter, lower, upper+1); break;
21006 case QCPFinancial::csCandlestick:
21007 drawCandlestickPlot(painter, lower, upper+1); break;
21011 /* inherits documentation from base class */
21012 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
21014 painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
21015 if (mChartStyle == csOhlc)
21017 if (mTwoColored)
21019 // draw upper left half icon with positive color:
21020 painter->setBrush(mBrushPositive);
21021 painter->setPen(mPenPositive);
21022 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21023 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21024 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21025 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21026 // draw bottom right hald icon with negative color:
21027 painter->setBrush(mBrushNegative);
21028 painter->setPen(mPenNegative);
21029 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21030 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21031 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21032 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21033 } else
21035 painter->setBrush(mBrush);
21036 painter->setPen(mPen);
21037 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21038 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21039 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21041 } else if (mChartStyle == csCandlestick)
21043 if (mTwoColored)
21045 // draw upper left half icon with positive color:
21046 painter->setBrush(mBrushPositive);
21047 painter->setPen(mPenPositive);
21048 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21049 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21050 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21051 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21052 // draw bottom right hald icon with negative color:
21053 painter->setBrush(mBrushNegative);
21054 painter->setPen(mPenNegative);
21055 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21056 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21057 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21058 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21059 } else
21061 painter->setBrush(mBrush);
21062 painter->setPen(mPen);
21063 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21064 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21065 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21070 /* inherits documentation from base class */
21071 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21073 QCPRange range;
21074 bool haveLower = false;
21075 bool haveUpper = false;
21077 double current;
21078 QCPFinancialDataMap::const_iterator it = mData->constBegin();
21079 while (it != mData->constEnd())
21081 current = it.value().key;
21082 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21084 if (current < range.lower || !haveLower)
21086 range.lower = current;
21087 haveLower = true;
21089 if (current > range.upper || !haveUpper)
21091 range.upper = current;
21092 haveUpper = true;
21095 ++it;
21097 // determine exact range by including width of bars/flags:
21098 if (haveLower && mKeyAxis)
21099 range.lower = range.lower-mWidth*0.5;
21100 if (haveUpper && mKeyAxis)
21101 range.upper = range.upper+mWidth*0.5;
21102 foundRange = haveLower && haveUpper;
21103 return range;
21106 /* inherits documentation from base class */
21107 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21109 QCPRange range;
21110 bool haveLower = false;
21111 bool haveUpper = false;
21113 QCPFinancialDataMap::const_iterator it = mData->constBegin();
21114 while (it != mData->constEnd())
21116 // high:
21117 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21119 if (it.value().high < range.lower || !haveLower)
21121 range.lower = it.value().high;
21122 haveLower = true;
21124 if (it.value().high > range.upper || !haveUpper)
21126 range.upper = it.value().high;
21127 haveUpper = true;
21130 // low:
21131 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21133 if (it.value().low < range.lower || !haveLower)
21135 range.lower = it.value().low;
21136 haveLower = true;
21138 if (it.value().low > range.upper || !haveUpper)
21140 range.upper = it.value().low;
21141 haveUpper = true;
21144 ++it;
21147 foundRange = haveLower && haveUpper;
21148 return range;
21151 /*! \internal
21153 Draws the data from \a begin to \a end as OHLC bars with the provided \a painter.
21155 This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc.
21157 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21159 QCPAxis *keyAxis = mKeyAxis.data();
21160 QCPAxis *valueAxis = mValueAxis.data();
21161 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21163 QPen linePen;
21165 if (keyAxis->orientation() == Qt::Horizontal)
21167 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21169 if (mSelected)
21170 linePen = mSelectedPen;
21171 else if (mTwoColored)
21172 linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21173 else
21174 linePen = mPen;
21175 painter->setPen(linePen);
21176 double keyPixel = keyAxis->coordToPixel(it.value().key);
21177 double openPixel = valueAxis->coordToPixel(it.value().open);
21178 double closePixel = valueAxis->coordToPixel(it.value().close);
21179 // draw backbone:
21180 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21181 // draw open:
21182 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21183 painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21184 // draw close:
21185 painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21187 } else
21189 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21191 if (mSelected)
21192 linePen = mSelectedPen;
21193 else if (mTwoColored)
21194 linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21195 else
21196 linePen = mPen;
21197 painter->setPen(linePen);
21198 double keyPixel = keyAxis->coordToPixel(it.value().key);
21199 double openPixel = valueAxis->coordToPixel(it.value().open);
21200 double closePixel = valueAxis->coordToPixel(it.value().close);
21201 // draw backbone:
21202 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21203 // draw open:
21204 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21205 painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21206 // draw close:
21207 painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21212 /*! \internal
21214 Draws the data from \a begin to \a end as Candlesticks with the provided \a painter.
21216 This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick.
21218 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21220 QCPAxis *keyAxis = mKeyAxis.data();
21221 QCPAxis *valueAxis = mValueAxis.data();
21222 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21224 QPen linePen;
21225 QBrush boxBrush;
21227 if (keyAxis->orientation() == Qt::Horizontal)
21229 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21231 if (mSelected)
21233 linePen = mSelectedPen;
21234 boxBrush = mSelectedBrush;
21235 } else if (mTwoColored)
21237 if (it.value().close >= it.value().open)
21239 linePen = mPenPositive;
21240 boxBrush = mBrushPositive;
21241 } else
21243 linePen = mPenNegative;
21244 boxBrush = mBrushNegative;
21246 } else
21248 linePen = mPen;
21249 boxBrush = mBrush;
21251 painter->setPen(linePen);
21252 painter->setBrush(boxBrush);
21253 double keyPixel = keyAxis->coordToPixel(it.value().key);
21254 double openPixel = valueAxis->coordToPixel(it.value().open);
21255 double closePixel = valueAxis->coordToPixel(it.value().close);
21256 // draw high:
21257 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21258 // draw low:
21259 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21260 // draw open-close box:
21261 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21262 painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21264 } else // keyAxis->orientation() == Qt::Vertical
21266 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21268 if (mSelected)
21270 linePen = mSelectedPen;
21271 boxBrush = mSelectedBrush;
21272 } else if (mTwoColored)
21274 if (it.value().close >= it.value().open)
21276 linePen = mPenPositive;
21277 boxBrush = mBrushPositive;
21278 } else
21280 linePen = mPenNegative;
21281 boxBrush = mBrushNegative;
21283 } else
21285 linePen = mPen;
21286 boxBrush = mBrush;
21288 painter->setPen(linePen);
21289 painter->setBrush(boxBrush);
21290 double keyPixel = keyAxis->coordToPixel(it.value().key);
21291 double openPixel = valueAxis->coordToPixel(it.value().open);
21292 double closePixel = valueAxis->coordToPixel(it.value().close);
21293 // draw high:
21294 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21295 // draw low:
21296 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21297 // draw open-close box:
21298 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21299 painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21304 /*! \internal
21306 This method is a helper function for \ref selectTest. It is used to test for selection when the
21307 chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end.
21309 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21311 QCPAxis *keyAxis = mKeyAxis.data();
21312 QCPAxis *valueAxis = mValueAxis.data();
21313 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21315 double minDistSqr = std::numeric_limits<double>::max();
21316 QCPFinancialDataMap::const_iterator it;
21317 if (keyAxis->orientation() == Qt::Horizontal)
21319 for (it = begin; it != end; ++it)
21321 double keyPixel = keyAxis->coordToPixel(it.value().key);
21322 // calculate distance to backbone:
21323 double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21324 if (currentDistSqr < minDistSqr)
21325 minDistSqr = currentDistSqr;
21327 } else // keyAxis->orientation() == Qt::Vertical
21329 for (it = begin; it != end; ++it)
21331 double keyPixel = keyAxis->coordToPixel(it.value().key);
21332 // calculate distance to backbone:
21333 double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21334 if (currentDistSqr < minDistSqr)
21335 minDistSqr = currentDistSqr;
21338 return qSqrt(minDistSqr);
21341 /*! \internal
21343 This method is a helper function for \ref selectTest. It is used to test for selection when the
21344 chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a
21345 end.
21347 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21349 QCPAxis *keyAxis = mKeyAxis.data();
21350 QCPAxis *valueAxis = mValueAxis.data();
21351 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21353 double minDistSqr = std::numeric_limits<double>::max();
21354 QCPFinancialDataMap::const_iterator it;
21355 if (keyAxis->orientation() == Qt::Horizontal)
21357 for (it = begin; it != end; ++it)
21359 double currentDistSqr;
21360 // determine whether pos is in open-close-box:
21361 QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21362 QCPRange boxValueRange(it.value().close, it.value().open);
21363 double posKey, posValue;
21364 pixelsToCoords(pos, posKey, posValue);
21365 if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21367 currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21368 } else
21370 // calculate distance to high/low lines:
21371 double keyPixel = keyAxis->coordToPixel(it.value().key);
21372 double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21373 double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21374 currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21376 if (currentDistSqr < minDistSqr)
21377 minDistSqr = currentDistSqr;
21379 } else // keyAxis->orientation() == Qt::Vertical
21381 for (it = begin; it != end; ++it)
21383 double currentDistSqr;
21384 // determine whether pos is in open-close-box:
21385 QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21386 QCPRange boxValueRange(it.value().close, it.value().open);
21387 double posKey, posValue;
21388 pixelsToCoords(pos, posKey, posValue);
21389 if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21391 currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21392 } else
21394 // calculate distance to high/low lines:
21395 double keyPixel = keyAxis->coordToPixel(it.value().key);
21396 double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21397 double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21398 currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21400 if (currentDistSqr < minDistSqr)
21401 minDistSqr = currentDistSqr;
21404 return qSqrt(minDistSqr);
21407 /*! \internal
21409 called by the drawing methods to determine which data (key) range is visible at the current key
21410 axis range setting, so only that needs to be processed.
21412 \a lower returns an iterator to the lowest data point that needs to be taken into account when
21413 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
21414 lower may still be just outside the visible range.
21416 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
21417 just outside of the visible range.
21419 if the plottable contains no data, both \a lower and \a upper point to constEnd.
21421 \see QCPGraph::getVisibleDataBounds
21423 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21425 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21426 if (mData->isEmpty())
21428 lower = mData->constEnd();
21429 upper = mData->constEnd();
21430 return;
21433 // get visible data range as QMap iterators
21434 QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
21435 QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
21436 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21437 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21439 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21440 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21444 ////////////////////////////////////////////////////////////////////////////////////////////////////
21445 //////////////////// QCPItemStraightLine
21446 ////////////////////////////////////////////////////////////////////////////////////////////////////
21448 /*! \class QCPItemStraightLine
21449 \brief A straight line that spans infinitely in both directions
21451 \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
21453 It has two positions, \a point1 and \a point2, which define the straight line.
21457 Creates a straight line item and sets default values.
21459 The constructed item can be added to the plot with QCustomPlot::addItem.
21461 QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
21462 QCPAbstractItem(parentPlot),
21463 point1(createPosition(QLatin1String("point1"))),
21464 point2(createPosition(QLatin1String("point2")))
21466 point1->setCoords(0, 0);
21467 point2->setCoords(1, 1);
21469 setPen(QPen(Qt::black));
21470 setSelectedPen(QPen(Qt::blue,2));
21473 QCPItemStraightLine::~QCPItemStraightLine()
21478 Sets the pen that will be used to draw the line
21480 \see setSelectedPen
21482 void QCPItemStraightLine::setPen(const QPen &pen)
21484 mPen = pen;
21488 Sets the pen that will be used to draw the line when selected
21490 \see setPen, setSelected
21492 void QCPItemStraightLine::setSelectedPen(const QPen &pen)
21494 mSelectedPen = pen;
21497 /* inherits documentation from base class */
21498 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21500 Q_UNUSED(details)
21501 if (onlySelectable && !mSelectable)
21502 return -1;
21504 return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21507 /* inherits documentation from base class */
21508 void QCPItemStraightLine::draw(QCPPainter *painter)
21510 QVector2D start(point1->pixelPoint());
21511 QVector2D end(point2->pixelPoint());
21512 // get visible segment of straight line inside clipRect:
21513 double clipPad = mainPen().widthF();
21514 QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21515 // paint visible segment, if existent:
21516 if (!line.isNull())
21518 painter->setPen(mainPen());
21519 painter->drawLine(line);
21523 /*! \internal
21525 finds the shortest distance of \a point to the straight line defined by the base point \a
21526 base and the direction vector \a vec.
21528 This is a helper function for \ref selectTest.
21530 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21532 return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21535 /*! \internal
21537 Returns the section of the straight line defined by \a base and direction vector \a
21538 vec, that is visible in the specified \a rect.
21540 This is a helper function for \ref draw.
21542 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21544 double bx, by;
21545 double gamma;
21546 QLineF result;
21547 if (vec.x() == 0 && vec.y() == 0)
21548 return result;
21549 if (qFuzzyIsNull(vec.x())) // line is vertical
21551 // check top of rect:
21552 bx = rect.left();
21553 by = rect.top();
21554 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21555 if (gamma >= 0 && gamma <= rect.width())
21556 result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21557 } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21559 // check left of rect:
21560 bx = rect.left();
21561 by = rect.top();
21562 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21563 if (gamma >= 0 && gamma <= rect.height())
21564 result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21565 } else // line is skewed
21567 QList<QVector2D> pointVectors;
21568 // check top of rect:
21569 bx = rect.left();
21570 by = rect.top();
21571 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21572 if (gamma >= 0 && gamma <= rect.width())
21573 pointVectors.append(QVector2D(bx+gamma, by));
21574 // check bottom of rect:
21575 bx = rect.left();
21576 by = rect.bottom();
21577 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21578 if (gamma >= 0 && gamma <= rect.width())
21579 pointVectors.append(QVector2D(bx+gamma, by));
21580 // check left of rect:
21581 bx = rect.left();
21582 by = rect.top();
21583 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21584 if (gamma >= 0 && gamma <= rect.height())
21585 pointVectors.append(QVector2D(bx, by+gamma));
21586 // check right of rect:
21587 bx = rect.right();
21588 by = rect.top();
21589 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21590 if (gamma >= 0 && gamma <= rect.height())
21591 pointVectors.append(QVector2D(bx, by+gamma));
21593 // evaluate points:
21594 if (pointVectors.size() == 2)
21596 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21597 } else if (pointVectors.size() > 2)
21599 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21600 double distSqrMax = 0;
21601 QVector2D pv1, pv2;
21602 for (int i=0; i<pointVectors.size()-1; ++i)
21604 for (int k=i+1; k<pointVectors.size(); ++k)
21606 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21607 if (distSqr > distSqrMax)
21609 pv1 = pointVectors.at(i);
21610 pv2 = pointVectors.at(k);
21611 distSqrMax = distSqr;
21615 result.setPoints(pv1.toPointF(), pv2.toPointF());
21618 return result;
21621 /*! \internal
21623 Returns the pen that should be used for drawing lines. Returns mPen when the
21624 item is not selected and mSelectedPen when it is.
21626 QPen QCPItemStraightLine::mainPen() const
21628 return mSelected ? mSelectedPen : mPen;
21632 ////////////////////////////////////////////////////////////////////////////////////////////////////
21633 //////////////////// QCPItemLine
21634 ////////////////////////////////////////////////////////////////////////////////////////////////////
21636 /*! \class QCPItemLine
21637 \brief A line from one point to another
21639 \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
21641 It has two positions, \a start and \a end, which define the end points of the line.
21643 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
21647 Creates a line item and sets default values.
21649 The constructed item can be added to the plot with QCustomPlot::addItem.
21651 QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
21652 QCPAbstractItem(parentPlot),
21653 start(createPosition(QLatin1String("start"))),
21654 end(createPosition(QLatin1String("end")))
21656 start->setCoords(0, 0);
21657 end->setCoords(1, 1);
21659 setPen(QPen(Qt::black));
21660 setSelectedPen(QPen(Qt::blue,2));
21663 QCPItemLine::~QCPItemLine()
21668 Sets the pen that will be used to draw the line
21670 \see setSelectedPen
21672 void QCPItemLine::setPen(const QPen &pen)
21674 mPen = pen;
21678 Sets the pen that will be used to draw the line when selected
21680 \see setPen, setSelected
21682 void QCPItemLine::setSelectedPen(const QPen &pen)
21684 mSelectedPen = pen;
21688 Sets the line ending style of the head. The head corresponds to the \a end position.
21690 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21691 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21693 \see setTail
21695 void QCPItemLine::setHead(const QCPLineEnding &head)
21697 mHead = head;
21701 Sets the line ending style of the tail. The tail corresponds to the \a start position.
21703 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21704 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21706 \see setHead
21708 void QCPItemLine::setTail(const QCPLineEnding &tail)
21710 mTail = tail;
21713 /* inherits documentation from base class */
21714 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21716 Q_UNUSED(details)
21717 if (onlySelectable && !mSelectable)
21718 return -1;
21720 return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
21723 /* inherits documentation from base class */
21724 void QCPItemLine::draw(QCPPainter *painter)
21726 QVector2D startVec(start->pixelPoint());
21727 QVector2D endVec(end->pixelPoint());
21728 if (startVec.toPoint() == endVec.toPoint())
21729 return;
21730 // get visible segment of straight line inside clipRect:
21731 double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
21732 clipPad = qMax(clipPad, (double)mainPen().widthF());
21733 QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21734 // paint visible segment, if existent:
21735 if (!line.isNull())
21737 painter->setPen(mainPen());
21738 painter->drawLine(line);
21739 painter->setBrush(Qt::SolidPattern);
21740 if (mTail.style() != QCPLineEnding::esNone)
21741 mTail.draw(painter, startVec, startVec-endVec);
21742 if (mHead.style() != QCPLineEnding::esNone)
21743 mHead.draw(painter, endVec, endVec-startVec);
21747 /*! \internal
21749 Returns the section of the line defined by \a start and \a end, that is visible in the specified
21750 \a rect.
21752 This is a helper function for \ref draw.
21754 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
21756 bool containsStart = rect.contains(start.x(), start.y());
21757 bool containsEnd = rect.contains(end.x(), end.y());
21758 if (containsStart && containsEnd)
21759 return QLineF(start.toPointF(), end.toPointF());
21761 QVector2D base = start;
21762 QVector2D vec = end-start;
21763 double bx, by;
21764 double gamma, mu;
21765 QLineF result;
21766 QList<QVector2D> pointVectors;
21768 if (!qFuzzyIsNull(vec.y())) // line is not horizontal
21770 // check top of rect:
21771 bx = rect.left();
21772 by = rect.top();
21773 mu = (by-base.y())/vec.y();
21774 if (mu >= 0 && mu <= 1)
21776 gamma = base.x()-bx + mu*vec.x();
21777 if (gamma >= 0 && gamma <= rect.width())
21778 pointVectors.append(QVector2D(bx+gamma, by));
21780 // check bottom of rect:
21781 bx = rect.left();
21782 by = rect.bottom();
21783 mu = (by-base.y())/vec.y();
21784 if (mu >= 0 && mu <= 1)
21786 gamma = base.x()-bx + mu*vec.x();
21787 if (gamma >= 0 && gamma <= rect.width())
21788 pointVectors.append(QVector2D(bx+gamma, by));
21791 if (!qFuzzyIsNull(vec.x())) // line is not vertical
21793 // check left of rect:
21794 bx = rect.left();
21795 by = rect.top();
21796 mu = (bx-base.x())/vec.x();
21797 if (mu >= 0 && mu <= 1)
21799 gamma = base.y()-by + mu*vec.y();
21800 if (gamma >= 0 && gamma <= rect.height())
21801 pointVectors.append(QVector2D(bx, by+gamma));
21803 // check right of rect:
21804 bx = rect.right();
21805 by = rect.top();
21806 mu = (bx-base.x())/vec.x();
21807 if (mu >= 0 && mu <= 1)
21809 gamma = base.y()-by + mu*vec.y();
21810 if (gamma >= 0 && gamma <= rect.height())
21811 pointVectors.append(QVector2D(bx, by+gamma));
21815 if (containsStart)
21816 pointVectors.append(start);
21817 if (containsEnd)
21818 pointVectors.append(end);
21820 // evaluate points:
21821 if (pointVectors.size() == 2)
21823 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21824 } else if (pointVectors.size() > 2)
21826 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21827 double distSqrMax = 0;
21828 QVector2D pv1, pv2;
21829 for (int i=0; i<pointVectors.size()-1; ++i)
21831 for (int k=i+1; k<pointVectors.size(); ++k)
21833 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21834 if (distSqr > distSqrMax)
21836 pv1 = pointVectors.at(i);
21837 pv2 = pointVectors.at(k);
21838 distSqrMax = distSqr;
21842 result.setPoints(pv1.toPointF(), pv2.toPointF());
21844 return result;
21847 /*! \internal
21849 Returns the pen that should be used for drawing lines. Returns mPen when the
21850 item is not selected and mSelectedPen when it is.
21852 QPen QCPItemLine::mainPen() const
21854 return mSelected ? mSelectedPen : mPen;
21858 ////////////////////////////////////////////////////////////////////////////////////////////////////
21859 //////////////////// QCPItemCurve
21860 ////////////////////////////////////////////////////////////////////////////////////////////////////
21862 /*! \class QCPItemCurve
21863 \brief A curved line from one point to another
21865 \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
21867 It has four positions, \a start and \a end, which define the end points of the line, and two
21868 control points which define the direction the line exits from the start and the direction from
21869 which it approaches the end: \a startDir and \a endDir.
21871 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
21872 arrow.
21874 Often it is desirable for the control points to stay at fixed relative positions to the start/end
21875 point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
21876 and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
21880 Creates a curve item and sets default values.
21882 The constructed item can be added to the plot with QCustomPlot::addItem.
21884 QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
21885 QCPAbstractItem(parentPlot),
21886 start(createPosition(QLatin1String("start"))),
21887 startDir(createPosition(QLatin1String("startDir"))),
21888 endDir(createPosition(QLatin1String("endDir"))),
21889 end(createPosition(QLatin1String("end")))
21891 start->setCoords(0, 0);
21892 startDir->setCoords(0.5, 0);
21893 endDir->setCoords(0, 0.5);
21894 end->setCoords(1, 1);
21896 setPen(QPen(Qt::black));
21897 setSelectedPen(QPen(Qt::blue,2));
21900 QCPItemCurve::~QCPItemCurve()
21905 Sets the pen that will be used to draw the line
21907 \see setSelectedPen
21909 void QCPItemCurve::setPen(const QPen &pen)
21911 mPen = pen;
21915 Sets the pen that will be used to draw the line when selected
21917 \see setPen, setSelected
21919 void QCPItemCurve::setSelectedPen(const QPen &pen)
21921 mSelectedPen = pen;
21925 Sets the line ending style of the head. The head corresponds to the \a end position.
21927 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21928 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21930 \see setTail
21932 void QCPItemCurve::setHead(const QCPLineEnding &head)
21934 mHead = head;
21938 Sets the line ending style of the tail. The tail corresponds to the \a start position.
21940 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21941 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21943 \see setHead
21945 void QCPItemCurve::setTail(const QCPLineEnding &tail)
21947 mTail = tail;
21950 /* inherits documentation from base class */
21951 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21953 Q_UNUSED(details)
21954 if (onlySelectable && !mSelectable)
21955 return -1;
21957 QPointF startVec(start->pixelPoint());
21958 QPointF startDirVec(startDir->pixelPoint());
21959 QPointF endDirVec(endDir->pixelPoint());
21960 QPointF endVec(end->pixelPoint());
21962 QPainterPath cubicPath(startVec);
21963 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21965 QPolygonF polygon = cubicPath.toSubpathPolygons().first();
21966 double minDistSqr = std::numeric_limits<double>::max();
21967 for (int i=1; i<polygon.size(); ++i)
21969 double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
21970 if (distSqr < minDistSqr)
21971 minDistSqr = distSqr;
21973 return qSqrt(minDistSqr);
21976 /* inherits documentation from base class */
21977 void QCPItemCurve::draw(QCPPainter *painter)
21979 QPointF startVec(start->pixelPoint());
21980 QPointF startDirVec(startDir->pixelPoint());
21981 QPointF endDirVec(endDir->pixelPoint());
21982 QPointF endVec(end->pixelPoint());
21983 if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
21984 return;
21986 QPainterPath cubicPath(startVec);
21987 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21989 // paint visible segment, if existent:
21990 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
21991 QRect cubicRect = cubicPath.controlPointRect().toRect();
21992 if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
21993 cubicRect.adjust(0, 0, 1, 1);
21994 if (clip.intersects(cubicRect))
21996 painter->setPen(mainPen());
21997 painter->drawPath(cubicPath);
21998 painter->setBrush(Qt::SolidPattern);
21999 if (mTail.style() != QCPLineEnding::esNone)
22000 mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
22001 if (mHead.style() != QCPLineEnding::esNone)
22002 mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
22006 /*! \internal
22008 Returns the pen that should be used for drawing lines. Returns mPen when the
22009 item is not selected and mSelectedPen when it is.
22011 QPen QCPItemCurve::mainPen() const
22013 return mSelected ? mSelectedPen : mPen;
22017 ////////////////////////////////////////////////////////////////////////////////////////////////////
22018 //////////////////// QCPItemRect
22019 ////////////////////////////////////////////////////////////////////////////////////////////////////
22021 /*! \class QCPItemRect
22022 \brief A rectangle
22024 \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
22026 It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
22030 Creates a rectangle item and sets default values.
22032 The constructed item can be added to the plot with QCustomPlot::addItem.
22034 QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
22035 QCPAbstractItem(parentPlot),
22036 topLeft(createPosition(QLatin1String("topLeft"))),
22037 bottomRight(createPosition(QLatin1String("bottomRight"))),
22038 top(createAnchor(QLatin1String("top"), aiTop)),
22039 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22040 right(createAnchor(QLatin1String("right"), aiRight)),
22041 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22042 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22043 left(createAnchor(QLatin1String("left"), aiLeft))
22045 topLeft->setCoords(0, 1);
22046 bottomRight->setCoords(1, 0);
22048 setPen(QPen(Qt::black));
22049 setSelectedPen(QPen(Qt::blue,2));
22050 setBrush(Qt::NoBrush);
22051 setSelectedBrush(Qt::NoBrush);
22054 QCPItemRect::~QCPItemRect()
22059 Sets the pen that will be used to draw the line of the rectangle
22061 \see setSelectedPen, setBrush
22063 void QCPItemRect::setPen(const QPen &pen)
22065 mPen = pen;
22069 Sets the pen that will be used to draw the line of the rectangle when selected
22071 \see setPen, setSelected
22073 void QCPItemRect::setSelectedPen(const QPen &pen)
22075 mSelectedPen = pen;
22079 Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
22080 Qt::NoBrush.
22082 \see setSelectedBrush, setPen
22084 void QCPItemRect::setBrush(const QBrush &brush)
22086 mBrush = brush;
22090 Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
22091 brush to Qt::NoBrush.
22093 \see setBrush
22095 void QCPItemRect::setSelectedBrush(const QBrush &brush)
22097 mSelectedBrush = brush;
22100 /* inherits documentation from base class */
22101 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22103 Q_UNUSED(details)
22104 if (onlySelectable && !mSelectable)
22105 return -1;
22107 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22108 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22109 return rectSelectTest(rect, pos, filledRect);
22112 /* inherits documentation from base class */
22113 void QCPItemRect::draw(QCPPainter *painter)
22115 QPointF p1 = topLeft->pixelPoint();
22116 QPointF p2 = bottomRight->pixelPoint();
22117 if (p1.toPoint() == p2.toPoint())
22118 return;
22119 QRectF rect = QRectF(p1, p2).normalized();
22120 double clipPad = mainPen().widthF();
22121 QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22122 if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22124 painter->setPen(mainPen());
22125 painter->setBrush(mainBrush());
22126 painter->drawRect(rect);
22130 /* inherits documentation from base class */
22131 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22133 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22134 switch (anchorId)
22136 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22137 case aiTopRight: return rect.topRight();
22138 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22139 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22140 case aiBottomLeft: return rect.bottomLeft();
22141 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22144 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22145 return QPointF();
22148 /*! \internal
22150 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22151 and mSelectedPen when it is.
22153 QPen QCPItemRect::mainPen() const
22155 return mSelected ? mSelectedPen : mPen;
22158 /*! \internal
22160 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22161 is not selected and mSelectedBrush when it is.
22163 QBrush QCPItemRect::mainBrush() const
22165 return mSelected ? mSelectedBrush : mBrush;
22169 ////////////////////////////////////////////////////////////////////////////////////////////////////
22170 //////////////////// QCPItemText
22171 ////////////////////////////////////////////////////////////////////////////////////////////////////
22173 /*! \class QCPItemText
22174 \brief A text label
22176 \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
22178 Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
22179 The latter controls which part of the text rect shall be aligned with \a position.
22181 The text alignment itself (i.e. left, center, right) can be controlled with \ref
22182 setTextAlignment.
22184 The text may be rotated around the \a position point with \ref setRotation.
22188 Creates a text item and sets default values.
22190 The constructed item can be added to the plot with QCustomPlot::addItem.
22192 QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
22193 QCPAbstractItem(parentPlot),
22194 position(createPosition(QLatin1String("position"))),
22195 topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22196 top(createAnchor(QLatin1String("top"), aiTop)),
22197 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22198 right(createAnchor(QLatin1String("right"), aiRight)),
22199 bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22200 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22201 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22202 left(createAnchor(QLatin1String("left"), aiLeft))
22204 position->setCoords(0, 0);
22206 setRotation(0);
22207 setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22208 setPositionAlignment(Qt::AlignCenter);
22209 setText(QLatin1String("text"));
22211 setPen(Qt::NoPen);
22212 setSelectedPen(Qt::NoPen);
22213 setBrush(Qt::NoBrush);
22214 setSelectedBrush(Qt::NoBrush);
22215 setColor(Qt::black);
22216 setSelectedColor(Qt::blue);
22219 QCPItemText::~QCPItemText()
22224 Sets the color of the text.
22226 void QCPItemText::setColor(const QColor &color)
22228 mColor = color;
22232 Sets the color of the text that will be used when the item is selected.
22234 void QCPItemText::setSelectedColor(const QColor &color)
22236 mSelectedColor = color;
22240 Sets the pen that will be used do draw a rectangular border around the text. To disable the
22241 border, set \a pen to Qt::NoPen.
22243 \see setSelectedPen, setBrush, setPadding
22245 void QCPItemText::setPen(const QPen &pen)
22247 mPen = pen;
22251 Sets the pen that will be used do draw a rectangular border around the text, when the item is
22252 selected. To disable the border, set \a pen to Qt::NoPen.
22254 \see setPen
22256 void QCPItemText::setSelectedPen(const QPen &pen)
22258 mSelectedPen = pen;
22262 Sets the brush that will be used do fill the background of the text. To disable the
22263 background, set \a brush to Qt::NoBrush.
22265 \see setSelectedBrush, setPen, setPadding
22267 void QCPItemText::setBrush(const QBrush &brush)
22269 mBrush = brush;
22273 Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
22274 background, set \a brush to Qt::NoBrush.
22276 \see setBrush
22278 void QCPItemText::setSelectedBrush(const QBrush &brush)
22280 mSelectedBrush = brush;
22284 Sets the font of the text.
22286 \see setSelectedFont, setColor
22288 void QCPItemText::setFont(const QFont &font)
22290 mFont = font;
22294 Sets the font of the text that will be used when the item is selected.
22296 \see setFont
22298 void QCPItemText::setSelectedFont(const QFont &font)
22300 mSelectedFont = font;
22304 Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
22305 character, e.g. '\n'.
22307 \see setFont, setColor, setTextAlignment
22309 void QCPItemText::setText(const QString &text)
22311 mText = text;
22315 Sets which point of the text rect shall be aligned with \a position.
22317 Examples:
22318 \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
22319 that the top of the text rect will be horizontally centered on \a position.
22320 \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
22321 bottom left corner of the text rect.
22323 If you want to control the alignment of (multi-lined) text within the text rect, use \ref
22324 setTextAlignment.
22326 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22328 mPositionAlignment = alignment;
22332 Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
22334 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22336 mTextAlignment = alignment;
22340 Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
22341 around \a position.
22343 void QCPItemText::setRotation(double degrees)
22345 mRotation = degrees;
22349 Sets the distance between the border of the text rectangle and the text. The appearance (and
22350 visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
22352 void QCPItemText::setPadding(const QMargins &padding)
22354 mPadding = padding;
22357 /* inherits documentation from base class */
22358 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22360 Q_UNUSED(details)
22361 if (onlySelectable && !mSelectable)
22362 return -1;
22364 // The rect may be rotated, so we transform the actual clicked pos to the rotated
22365 // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22366 QPointF positionPixels(position->pixelPoint());
22367 QTransform inputTransform;
22368 inputTransform.translate(positionPixels.x(), positionPixels.y());
22369 inputTransform.rotate(-mRotation);
22370 inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22371 QPointF rotatedPos = inputTransform.map(pos);
22372 QFontMetrics fontMetrics(mFont);
22373 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22374 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22375 QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22376 textBoxRect.moveTopLeft(textPos.toPoint());
22378 return rectSelectTest(textBoxRect, rotatedPos, true);
22381 /* inherits documentation from base class */
22382 void QCPItemText::draw(QCPPainter *painter)
22384 QPointF pos(position->pixelPoint());
22385 QTransform transform = painter->transform();
22386 transform.translate(pos.x(), pos.y());
22387 if (!qFuzzyIsNull(mRotation))
22388 transform.rotate(mRotation);
22389 painter->setFont(mainFont());
22390 QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22391 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22392 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22393 textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22394 textBoxRect.moveTopLeft(textPos.toPoint());
22395 double clipPad = mainPen().widthF();
22396 QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22397 if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22399 painter->setTransform(transform);
22400 if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22401 (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22403 painter->setPen(mainPen());
22404 painter->setBrush(mainBrush());
22405 painter->drawRect(textBoxRect);
22407 painter->setBrush(Qt::NoBrush);
22408 painter->setPen(QPen(mainColor()));
22409 painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22413 /* inherits documentation from base class */
22414 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22416 // get actual rect points (pretty much copied from draw function):
22417 QPointF pos(position->pixelPoint());
22418 QTransform transform;
22419 transform.translate(pos.x(), pos.y());
22420 if (!qFuzzyIsNull(mRotation))
22421 transform.rotate(mRotation);
22422 QFontMetrics fontMetrics(mainFont());
22423 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22424 QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22425 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22426 textBoxRect.moveTopLeft(textPos.toPoint());
22427 QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22429 switch (anchorId)
22431 case aiTopLeft: return rectPoly.at(0);
22432 case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22433 case aiTopRight: return rectPoly.at(1);
22434 case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22435 case aiBottomRight: return rectPoly.at(2);
22436 case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22437 case aiBottomLeft: return rectPoly.at(3);
22438 case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22441 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22442 return QPointF();
22445 /*! \internal
22447 Returns the point that must be given to the QPainter::drawText function (which expects the top
22448 left point of the text rect), according to the position \a pos, the text bounding box \a rect and
22449 the requested \a positionAlignment.
22451 For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
22452 will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
22453 drawn at that point, the lower left corner of the resulting text rect is at \a pos.
22455 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22457 if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22458 return pos;
22460 QPointF result = pos; // start at top left
22461 if (positionAlignment.testFlag(Qt::AlignHCenter))
22462 result.rx() -= rect.width()/2.0;
22463 else if (positionAlignment.testFlag(Qt::AlignRight))
22464 result.rx() -= rect.width();
22465 if (positionAlignment.testFlag(Qt::AlignVCenter))
22466 result.ry() -= rect.height()/2.0;
22467 else if (positionAlignment.testFlag(Qt::AlignBottom))
22468 result.ry() -= rect.height();
22469 return result;
22472 /*! \internal
22474 Returns the font that should be used for drawing text. Returns mFont when the item is not selected
22475 and mSelectedFont when it is.
22477 QFont QCPItemText::mainFont() const
22479 return mSelected ? mSelectedFont : mFont;
22482 /*! \internal
22484 Returns the color that should be used for drawing text. Returns mColor when the item is not
22485 selected and mSelectedColor when it is.
22487 QColor QCPItemText::mainColor() const
22489 return mSelected ? mSelectedColor : mColor;
22492 /*! \internal
22494 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22495 and mSelectedPen when it is.
22497 QPen QCPItemText::mainPen() const
22499 return mSelected ? mSelectedPen : mPen;
22502 /*! \internal
22504 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22505 is not selected and mSelectedBrush when it is.
22507 QBrush QCPItemText::mainBrush() const
22509 return mSelected ? mSelectedBrush : mBrush;
22513 ////////////////////////////////////////////////////////////////////////////////////////////////////
22514 //////////////////// QCPItemEllipse
22515 ////////////////////////////////////////////////////////////////////////////////////////////////////
22517 /*! \class QCPItemEllipse
22518 \brief An ellipse
22520 \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
22522 It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
22526 Creates an ellipse item and sets default values.
22528 The constructed item can be added to the plot with QCustomPlot::addItem.
22530 QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
22531 QCPAbstractItem(parentPlot),
22532 topLeft(createPosition(QLatin1String("topLeft"))),
22533 bottomRight(createPosition(QLatin1String("bottomRight"))),
22534 topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22535 top(createAnchor(QLatin1String("top"), aiTop)),
22536 topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22537 right(createAnchor(QLatin1String("right"), aiRight)),
22538 bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22539 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22540 bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22541 left(createAnchor(QLatin1String("left"), aiLeft)),
22542 center(createAnchor(QLatin1String("center"), aiCenter))
22544 topLeft->setCoords(0, 1);
22545 bottomRight->setCoords(1, 0);
22547 setPen(QPen(Qt::black));
22548 setSelectedPen(QPen(Qt::blue, 2));
22549 setBrush(Qt::NoBrush);
22550 setSelectedBrush(Qt::NoBrush);
22553 QCPItemEllipse::~QCPItemEllipse()
22558 Sets the pen that will be used to draw the line of the ellipse
22560 \see setSelectedPen, setBrush
22562 void QCPItemEllipse::setPen(const QPen &pen)
22564 mPen = pen;
22568 Sets the pen that will be used to draw the line of the ellipse when selected
22570 \see setPen, setSelected
22572 void QCPItemEllipse::setSelectedPen(const QPen &pen)
22574 mSelectedPen = pen;
22578 Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
22579 Qt::NoBrush.
22581 \see setSelectedBrush, setPen
22583 void QCPItemEllipse::setBrush(const QBrush &brush)
22585 mBrush = brush;
22589 Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
22590 brush to Qt::NoBrush.
22592 \see setBrush
22594 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
22596 mSelectedBrush = brush;
22599 /* inherits documentation from base class */
22600 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22602 Q_UNUSED(details)
22603 if (onlySelectable && !mSelectable)
22604 return -1;
22606 double result = -1;
22607 QPointF p1 = topLeft->pixelPoint();
22608 QPointF p2 = bottomRight->pixelPoint();
22609 QPointF center((p1+p2)/2.0);
22610 double a = qAbs(p1.x()-p2.x())/2.0;
22611 double b = qAbs(p1.y()-p2.y())/2.0;
22612 double x = pos.x()-center.x();
22613 double y = pos.y()-center.y();
22615 // distance to border:
22616 double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
22617 result = qAbs(c-1)*qSqrt(x*x+y*y);
22618 // filled ellipse, allow click inside to count as hit:
22619 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
22621 if (x*x/(a*a) + y*y/(b*b) <= 1)
22622 result = mParentPlot->selectionTolerance()*0.99;
22624 return result;
22627 /* inherits documentation from base class */
22628 void QCPItemEllipse::draw(QCPPainter *painter)
22630 QPointF p1 = topLeft->pixelPoint();
22631 QPointF p2 = bottomRight->pixelPoint();
22632 if (p1.toPoint() == p2.toPoint())
22633 return;
22634 QRectF ellipseRect = QRectF(p1, p2).normalized();
22635 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22636 if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
22638 painter->setPen(mainPen());
22639 painter->setBrush(mainBrush());
22640 #ifdef __EXCEPTIONS
22641 try // drawEllipse sometimes throws exceptions if ellipse is too big
22643 #endif
22644 painter->drawEllipse(ellipseRect);
22645 #ifdef __EXCEPTIONS
22646 } catch (...)
22648 qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
22649 setVisible(false);
22651 #endif
22655 /* inherits documentation from base class */
22656 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
22658 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22659 switch (anchorId)
22661 case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
22662 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22663 case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
22664 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22665 case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
22666 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22667 case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
22668 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22669 case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
22672 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22673 return QPointF();
22676 /*! \internal
22678 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22679 and mSelectedPen when it is.
22681 QPen QCPItemEllipse::mainPen() const
22683 return mSelected ? mSelectedPen : mPen;
22686 /*! \internal
22688 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22689 is not selected and mSelectedBrush when it is.
22691 QBrush QCPItemEllipse::mainBrush() const
22693 return mSelected ? mSelectedBrush : mBrush;
22697 ////////////////////////////////////////////////////////////////////////////////////////////////////
22698 //////////////////// QCPItemPixmap
22699 ////////////////////////////////////////////////////////////////////////////////////////////////////
22701 /*! \class QCPItemPixmap
22702 \brief An arbitrary pixmap
22704 \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
22706 It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
22707 be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
22708 fit the rectangle or be drawn aligned to the topLeft position.
22710 If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
22711 on the right side of the example image), the pixmap will be flipped in the respective
22712 orientations.
22716 Creates a rectangle item and sets default values.
22718 The constructed item can be added to the plot with QCustomPlot::addItem.
22720 QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
22721 QCPAbstractItem(parentPlot),
22722 topLeft(createPosition(QLatin1String("topLeft"))),
22723 bottomRight(createPosition(QLatin1String("bottomRight"))),
22724 top(createAnchor(QLatin1String("top"), aiTop)),
22725 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22726 right(createAnchor(QLatin1String("right"), aiRight)),
22727 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22728 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22729 left(createAnchor(QLatin1String("left"), aiLeft))
22731 topLeft->setCoords(0, 1);
22732 bottomRight->setCoords(1, 0);
22734 setPen(Qt::NoPen);
22735 setSelectedPen(QPen(Qt::blue));
22736 setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
22739 QCPItemPixmap::~QCPItemPixmap()
22744 Sets the pixmap that will be displayed.
22746 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
22748 mPixmap = pixmap;
22749 if (mPixmap.isNull())
22750 qDebug() << Q_FUNC_INFO << "pixmap is null";
22754 Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
22755 bottomRight positions.
22757 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
22759 mScaled = scaled;
22760 mAspectRatioMode = aspectRatioMode;
22761 mTransformationMode = transformationMode;
22762 updateScaledPixmap();
22766 Sets the pen that will be used to draw a border around the pixmap.
22768 \see setSelectedPen, setBrush
22770 void QCPItemPixmap::setPen(const QPen &pen)
22772 mPen = pen;
22776 Sets the pen that will be used to draw a border around the pixmap when selected
22778 \see setPen, setSelected
22780 void QCPItemPixmap::setSelectedPen(const QPen &pen)
22782 mSelectedPen = pen;
22785 /* inherits documentation from base class */
22786 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22788 Q_UNUSED(details)
22789 if (onlySelectable && !mSelectable)
22790 return -1;
22792 return rectSelectTest(getFinalRect(), pos, true);
22795 /* inherits documentation from base class */
22796 void QCPItemPixmap::draw(QCPPainter *painter)
22798 bool flipHorz = false;
22799 bool flipVert = false;
22800 QRect rect = getFinalRect(&flipHorz, &flipVert);
22801 double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
22802 QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22803 if (boundingRect.intersects(clipRect()))
22805 updateScaledPixmap(rect, flipHorz, flipVert);
22806 painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
22807 QPen pen = mainPen();
22808 if (pen.style() != Qt::NoPen)
22810 painter->setPen(pen);
22811 painter->setBrush(Qt::NoBrush);
22812 painter->drawRect(rect);
22817 /* inherits documentation from base class */
22818 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
22820 bool flipHorz;
22821 bool flipVert;
22822 QRect rect = getFinalRect(&flipHorz, &flipVert);
22823 // we actually want denormal rects (negative width/height) here, so restore
22824 // the flipped state:
22825 if (flipHorz)
22826 rect.adjust(rect.width(), 0, -rect.width(), 0);
22827 if (flipVert)
22828 rect.adjust(0, rect.height(), 0, -rect.height());
22830 switch (anchorId)
22832 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22833 case aiTopRight: return rect.topRight();
22834 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22835 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22836 case aiBottomLeft: return rect.bottomLeft();
22837 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
22840 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22841 return QPointF();
22844 /*! \internal
22846 Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
22847 parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
22848 horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
22849 bottomRight.)
22851 This function only creates the scaled pixmap when the buffered pixmap has a different size than
22852 the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
22853 not cause expensive rescaling every time.
22855 If scaling is disabled, sets mScaledPixmap to a null QPixmap.
22857 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
22859 if (mPixmap.isNull())
22860 return;
22862 if (mScaled)
22864 if (finalRect.isNull())
22865 finalRect = getFinalRect(&flipHorz, &flipVert);
22866 if (finalRect.size() != mScaledPixmap.size())
22868 mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
22869 if (flipHorz || flipVert)
22870 mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
22872 } else if (!mScaledPixmap.isNull())
22873 mScaledPixmap = QPixmap();
22876 /*! \internal
22878 Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
22879 and scaling settings.
22881 The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
22882 flipped horizontally or vertically in the returned rect. (The returned rect itself is always
22883 normalized, i.e. the top left corner of the rect is actually further to the top/left than the
22884 bottom right corner). This is the case when the item position \a topLeft is further to the
22885 bottom/right than \a bottomRight.
22887 If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
22888 aligned with the item position \a topLeft. The position \a bottomRight is ignored.
22890 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
22892 QRect result;
22893 bool flipHorz = false;
22894 bool flipVert = false;
22895 QPoint p1 = topLeft->pixelPoint().toPoint();
22896 QPoint p2 = bottomRight->pixelPoint().toPoint();
22897 if (p1 == p2)
22898 return QRect(p1, QSize(0, 0));
22899 if (mScaled)
22901 QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
22902 QPoint topLeft = p1;
22903 if (newSize.width() < 0)
22905 flipHorz = true;
22906 newSize.rwidth() *= -1;
22907 topLeft.setX(p2.x());
22909 if (newSize.height() < 0)
22911 flipVert = true;
22912 newSize.rheight() *= -1;
22913 topLeft.setY(p2.y());
22915 QSize scaledSize = mPixmap.size();
22916 scaledSize.scale(newSize, mAspectRatioMode);
22917 result = QRect(topLeft, scaledSize);
22918 } else
22920 result = QRect(p1, mPixmap.size());
22922 if (flippedHorz)
22923 *flippedHorz = flipHorz;
22924 if (flippedVert)
22925 *flippedVert = flipVert;
22926 return result;
22929 /*! \internal
22931 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22932 and mSelectedPen when it is.
22934 QPen QCPItemPixmap::mainPen() const
22936 return mSelected ? mSelectedPen : mPen;
22940 ////////////////////////////////////////////////////////////////////////////////////////////////////
22941 //////////////////// QCPItemTracer
22942 ////////////////////////////////////////////////////////////////////////////////////////////////////
22944 /*! \class QCPItemTracer
22945 \brief Item that sticks to QCPGraph data points
22947 \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
22949 The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
22950 the coordinate axes of the graph and update its \a position to be on the graph's data. This means
22951 the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
22952 QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a
22953 position will have no effect because they will be overriden in the next redraw (this is when the
22954 coordinate update happens).
22956 If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
22957 stay at the corresponding end of the graph.
22959 With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
22960 points or whether it interpolates data points linearly, if given a key that lies between two data
22961 points of the graph.
22963 The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
22964 have no own visual appearance (set the style to \ref tsNone), and just connect other item
22965 positions to the tracer \a position (used as an anchor) via \ref
22966 QCPItemPosition::setParentAnchor.
22968 \note The tracer position is only automatically updated upon redraws. So when the data of the
22969 graph changes and immediately afterwards (without a redraw) the a position coordinates of the
22970 tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref
22971 updatePosition must be called manually, prior to reading the tracer coordinates.
22975 Creates a tracer item and sets default values.
22977 The constructed item can be added to the plot with QCustomPlot::addItem.
22979 QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
22980 QCPAbstractItem(parentPlot),
22981 position(createPosition(QLatin1String("position"))),
22982 mGraph(0)
22984 position->setCoords(0, 0);
22986 setBrush(Qt::NoBrush);
22987 setSelectedBrush(Qt::NoBrush);
22988 setPen(QPen(Qt::black));
22989 setSelectedPen(QPen(Qt::blue, 2));
22990 setStyle(tsCrosshair);
22991 setSize(6);
22992 setInterpolating(false);
22993 setGraphKey(0);
22996 QCPItemTracer::~QCPItemTracer()
23001 Sets the pen that will be used to draw the line of the tracer
23003 \see setSelectedPen, setBrush
23005 void QCPItemTracer::setPen(const QPen &pen)
23007 mPen = pen;
23011 Sets the pen that will be used to draw the line of the tracer when selected
23013 \see setPen, setSelected
23015 void QCPItemTracer::setSelectedPen(const QPen &pen)
23017 mSelectedPen = pen;
23021 Sets the brush that will be used to draw any fills of the tracer
23023 \see setSelectedBrush, setPen
23025 void QCPItemTracer::setBrush(const QBrush &brush)
23027 mBrush = brush;
23031 Sets the brush that will be used to draw any fills of the tracer, when selected.
23033 \see setBrush, setSelected
23035 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
23037 mSelectedBrush = brush;
23041 Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
23042 does, \ref tsCrosshair does not).
23044 void QCPItemTracer::setSize(double size)
23046 mSize = size;
23050 Sets the style/visual appearance of the tracer.
23052 If you only want to use the tracer \a position as an anchor for other items, set \a style to
23053 \ref tsNone.
23055 void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
23057 mStyle = style;
23061 Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
23062 QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
23064 To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
23065 freely like any other item position. This is the state the tracer will assume when its graph gets
23066 deleted while still attached to it.
23068 \see setGraphKey
23070 void QCPItemTracer::setGraph(QCPGraph *graph)
23072 if (graph)
23074 if (graph->parentPlot() == mParentPlot)
23076 position->setType(QCPItemPosition::ptPlotCoords);
23077 position->setAxes(graph->keyAxis(), graph->valueAxis());
23078 mGraph = graph;
23079 updatePosition();
23080 } else
23081 qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23082 } else
23084 mGraph = 0;
23089 Sets the key of the graph's data point the tracer will be positioned at. This is the only free
23090 coordinate of a tracer when attached to a graph.
23092 Depending on \ref setInterpolating, the tracer will be either positioned on the data point
23093 closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
23095 \see setGraph, setInterpolating
23097 void QCPItemTracer::setGraphKey(double key)
23099 mGraphKey = key;
23103 Sets whether the value of the graph's data points shall be interpolated, when positioning the
23104 tracer.
23106 If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
23107 the data point of the graph which is closest to the key, but which is not necessarily exactly
23108 there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
23109 the appropriate value will be interpolated from the graph's data points linearly.
23111 \see setGraph, setGraphKey
23113 void QCPItemTracer::setInterpolating(bool enabled)
23115 mInterpolating = enabled;
23118 /* inherits documentation from base class */
23119 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23121 Q_UNUSED(details)
23122 if (onlySelectable && !mSelectable)
23123 return -1;
23125 QPointF center(position->pixelPoint());
23126 double w = mSize/2.0;
23127 QRect clip = clipRect();
23128 switch (mStyle)
23130 case tsNone: return -1;
23131 case tsPlus:
23133 if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23134 return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23135 distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23136 break;
23138 case tsCrosshair:
23140 return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23141 distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23143 case tsCircle:
23145 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23147 // distance to border:
23148 double centerDist = QVector2D(center-pos).length();
23149 double circleLine = w;
23150 double result = qAbs(centerDist-circleLine);
23151 // filled ellipse, allow click inside to count as hit:
23152 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23154 if (centerDist <= circleLine)
23155 result = mParentPlot->selectionTolerance()*0.99;
23157 return result;
23159 break;
23161 case tsSquare:
23163 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23165 QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23166 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23167 return rectSelectTest(rect, pos, filledRect);
23169 break;
23172 return -1;
23175 /* inherits documentation from base class */
23176 void QCPItemTracer::draw(QCPPainter *painter)
23178 updatePosition();
23179 if (mStyle == tsNone)
23180 return;
23182 painter->setPen(mainPen());
23183 painter->setBrush(mainBrush());
23184 QPointF center(position->pixelPoint());
23185 double w = mSize/2.0;
23186 QRect clip = clipRect();
23187 switch (mStyle)
23189 case tsNone: return;
23190 case tsPlus:
23192 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23194 painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23195 painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23197 break;
23199 case tsCrosshair:
23201 if (center.y() > clip.top() && center.y() < clip.bottom())
23202 painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23203 if (center.x() > clip.left() && center.x() < clip.right())
23204 painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23205 break;
23207 case tsCircle:
23209 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23210 painter->drawEllipse(center, w, w);
23211 break;
23213 case tsSquare:
23215 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23216 painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23217 break;
23223 If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
23224 position to reside on the graph data, depending on the configured key (\ref setGraphKey).
23226 It is called automatically on every redraw and normally doesn't need to be called manually. One
23227 exception is when you want to read the tracer coordinates via \a position and are not sure that
23228 the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
23229 In that situation, call this function before accessing \a position, to make sure you don't get
23230 out-of-date coordinates.
23232 If there is no graph set on this tracer, this function does nothing.
23234 void QCPItemTracer::updatePosition()
23236 if (mGraph)
23238 if (mParentPlot->hasPlottable(mGraph))
23240 if (mGraph->data()->size() > 1)
23242 QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23243 QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
23244 if (mGraphKey < first.key())
23245 position->setCoords(first.key(), first.value().value);
23246 else if (mGraphKey > last.key())
23247 position->setCoords(last.key(), last.value().value);
23248 else
23250 QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23251 if (it != first) // mGraphKey is somewhere between iterators
23253 QCPDataMap::const_iterator prevIt = it-1;
23254 if (mInterpolating)
23256 // interpolate between iterators around mGraphKey:
23257 double slope = 0;
23258 if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23259 slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23260 position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23261 } else
23263 // find iterator with key closest to mGraphKey:
23264 if (mGraphKey < (prevIt.key()+it.key())*0.5)
23265 it = prevIt;
23266 position->setCoords(it.key(), it.value().value);
23268 } else // mGraphKey is exactly on first iterator
23269 position->setCoords(it.key(), it.value().value);
23271 } else if (mGraph->data()->size() == 1)
23273 QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23274 position->setCoords(it.key(), it.value().value);
23275 } else
23276 qDebug() << Q_FUNC_INFO << "graph has no data";
23277 } else
23278 qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23282 /*! \internal
23284 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
23285 and mSelectedPen when it is.
23287 QPen QCPItemTracer::mainPen() const
23289 return mSelected ? mSelectedPen : mPen;
23292 /*! \internal
23294 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
23295 is not selected and mSelectedBrush when it is.
23297 QBrush QCPItemTracer::mainBrush() const
23299 return mSelected ? mSelectedBrush : mBrush;
23303 ////////////////////////////////////////////////////////////////////////////////////////////////////
23304 //////////////////// QCPItemBracket
23305 ////////////////////////////////////////////////////////////////////////////////////////////////////
23307 /*! \class QCPItemBracket
23308 \brief A bracket for referencing/highlighting certain parts in the plot.
23310 \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
23312 It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
23313 actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
23314 example image.
23316 The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
23317 stretches away from the embraced span, can be controlled with \ref setLength.
23319 \image html QCPItemBracket-length.png
23320 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23321 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23323 It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
23324 or QCPItemCurve) or a text label (QCPItemText), to the bracket.
23328 Creates a bracket item and sets default values.
23330 The constructed item can be added to the plot with QCustomPlot::addItem.
23332 QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
23333 QCPAbstractItem(parentPlot),
23334 left(createPosition(QLatin1String("left"))),
23335 right(createPosition(QLatin1String("right"))),
23336 center(createAnchor(QLatin1String("center"), aiCenter))
23338 left->setCoords(0, 0);
23339 right->setCoords(1, 1);
23341 setPen(QPen(Qt::black));
23342 setSelectedPen(QPen(Qt::blue, 2));
23343 setLength(8);
23344 setStyle(bsCalligraphic);
23347 QCPItemBracket::~QCPItemBracket()
23352 Sets the pen that will be used to draw the bracket.
23354 Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
23355 stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
23356 \ref setLength, which has a similar effect.
23358 \see setSelectedPen
23360 void QCPItemBracket::setPen(const QPen &pen)
23362 mPen = pen;
23366 Sets the pen that will be used to draw the bracket when selected
23368 \see setPen, setSelected
23370 void QCPItemBracket::setSelectedPen(const QPen &pen)
23372 mSelectedPen = pen;
23376 Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
23377 span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
23379 \image html QCPItemBracket-length.png
23380 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23381 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23383 void QCPItemBracket::setLength(double length)
23385 mLength = length;
23389 Sets the style of the bracket, i.e. the shape/visual appearance.
23391 \see setPen
23393 void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
23395 mStyle = style;
23398 /* inherits documentation from base class */
23399 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23401 Q_UNUSED(details)
23402 if (onlySelectable && !mSelectable)
23403 return -1;
23405 QVector2D leftVec(left->pixelPoint());
23406 QVector2D rightVec(right->pixelPoint());
23407 if (leftVec.toPoint() == rightVec.toPoint())
23408 return -1;
23410 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23411 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23412 lengthVec = lengthVec.normalized()*mLength;
23413 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23415 switch (mStyle)
23417 case QCPItemBracket::bsSquare:
23418 case QCPItemBracket::bsRound:
23420 double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23421 double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos);
23422 double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
23423 return qSqrt(qMin(qMin(a, b), c));
23425 case QCPItemBracket::bsCurly:
23426 case QCPItemBracket::bsCalligraphic:
23428 double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23429 double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23430 double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
23431 double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
23432 return qSqrt(qMin(qMin(a, b), qMin(c, d)));
23435 return -1;
23438 /* inherits documentation from base class */
23439 void QCPItemBracket::draw(QCPPainter *painter)
23441 QVector2D leftVec(left->pixelPoint());
23442 QVector2D rightVec(right->pixelPoint());
23443 if (leftVec.toPoint() == rightVec.toPoint())
23444 return;
23446 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23447 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23448 lengthVec = lengthVec.normalized()*mLength;
23449 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23451 QPolygon boundingPoly;
23452 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23453 << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23454 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23455 if (clip.intersects(boundingPoly.boundingRect()))
23457 painter->setPen(mainPen());
23458 switch (mStyle)
23460 case bsSquare:
23462 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23463 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23464 painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23465 break;
23467 case bsRound:
23469 painter->setBrush(Qt::NoBrush);
23470 QPainterPath path;
23471 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23472 path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23473 path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23474 painter->drawPath(path);
23475 break;
23477 case bsCurly:
23479 painter->setBrush(Qt::NoBrush);
23480 QPainterPath path;
23481 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23482 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23483 path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23484 painter->drawPath(path);
23485 break;
23487 case bsCalligraphic:
23489 painter->setPen(Qt::NoPen);
23490 painter->setBrush(QBrush(mainPen().color()));
23491 QPainterPath path;
23492 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23494 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23495 path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23497 path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23498 path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23500 painter->drawPath(path);
23501 break;
23507 /* inherits documentation from base class */
23508 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23510 QVector2D leftVec(left->pixelPoint());
23511 QVector2D rightVec(right->pixelPoint());
23512 if (leftVec.toPoint() == rightVec.toPoint())
23513 return leftVec.toPointF();
23515 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23516 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23517 lengthVec = lengthVec.normalized()*mLength;
23518 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23520 switch (anchorId)
23522 case aiCenter:
23523 return centerVec.toPointF();
23525 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23526 return QPointF();
23529 /*! \internal
23531 Returns the pen that should be used for drawing lines. Returns mPen when the
23532 item is not selected and mSelectedPen when it is.
23534 QPen QCPItemBracket::mainPen() const
23536 return mSelected ? mSelectedPen : mPen;