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: 22.12.15 ** |
23 |
|
|
** Version: 1.3.2 ** |
24 |
|
|
****************************************************************************/ |
25 |
|
|
|
26 |
|
|
#include "qcustomplot.h" |
27 |
|
|
|
28 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
29 |
|
|
//////////////////// QCPPainter |
30 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
31 |
|
|
|
32 |
|
|
/*! \class QCPPainter |
33 |
|
|
\brief QPainter subclass used internally |
34 |
|
|
|
35 |
|
|
This QPainter subclass is used to provide some extended functionality e.g. for |
36 |
|
|
tweaking position consistency between antialiased and non-antialiased |
37 |
|
|
painting. Further it provides workarounds for QPainter quirks. |
38 |
|
|
|
39 |
|
|
\warning This class intentionally hides non-virtual functions of QPainter, |
40 |
|
|
e.g. setPen, save and restore. So while it is possible to pass a QCPPainter |
41 |
|
|
instance to a function that expects a QPainter pointer, some of the |
42 |
|
|
workarounds and tweaks will be unavailable to the function (because it will |
43 |
|
|
call the base class implementations of the functions actually hidden by |
44 |
|
|
QCPPainter). |
45 |
|
|
*/ |
46 |
|
|
|
47 |
|
|
/*! |
48 |
|
|
Creates a new QCPPainter instance and sets default values |
49 |
|
|
*/ |
50 |
|
|
QCPPainter::QCPPainter() |
51 |
|
|
: QPainter(), mModes(pmDefault), mIsAntialiasing(false) { |
52 |
|
|
// don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter |
53 |
|
|
// isn't active yet and a call to begin() will follow |
54 |
|
|
} |
55 |
|
|
|
56 |
|
|
/*! |
57 |
|
|
Creates a new QCPPainter instance on the specified paint \a device and sets |
58 |
|
|
default values. Just like the analogous QPainter constructor, begins painting |
59 |
|
|
on \a device immediately. |
60 |
|
|
|
61 |
|
|
Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt |
62 |
|
|
versions before Qt5. |
63 |
|
|
*/ |
64 |
|
|
QCPPainter::QCPPainter(QPaintDevice *device) |
65 |
|
|
: QPainter(device), mModes(pmDefault), mIsAntialiasing(false) { |
66 |
|
|
#if QT_VERSION < \ |
67 |
|
|
QT_VERSION_CHECK(5, 0, \ |
68 |
|
|
0) // before Qt5, default pens used to be cosmetic if |
69 |
|
|
// NonCosmeticDefaultPen flag isn't set. So we set it |
70 |
|
|
// to get consistency across Qt versions. |
71 |
|
|
if (isActive()) setRenderHint(QPainter::NonCosmeticDefaultPen); |
72 |
|
|
#endif |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
QCPPainter::~QCPPainter() {} |
76 |
|
|
|
77 |
|
|
/*! |
78 |
|
|
Sets the pen of the painter and applies certain fixes to it, depending on the |
79 |
|
|
mode of this QCPPainter. |
80 |
|
|
|
81 |
|
|
\note this function hides the non-virtual base class implementation. |
82 |
|
|
*/ |
83 |
|
|
void QCPPainter::setPen(const QPen &pen) { |
84 |
|
|
QPainter::setPen(pen); |
85 |
|
|
if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic(); |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
/*! \overload |
89 |
|
|
|
90 |
|
|
Sets the pen (by color) of the painter and applies certain fixes to it, |
91 |
|
|
depending on the mode of this QCPPainter. |
92 |
|
|
|
93 |
|
|
\note this function hides the non-virtual base class implementation. |
94 |
|
|
*/ |
95 |
|
|
void QCPPainter::setPen(const QColor &color) { |
96 |
|
|
QPainter::setPen(color); |
97 |
|
|
if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic(); |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
/*! \overload |
101 |
|
|
|
102 |
|
|
Sets the pen (by style) of the painter and applies certain fixes to it, |
103 |
|
|
depending on the mode of this QCPPainter. |
104 |
|
|
|
105 |
|
|
\note this function hides the non-virtual base class implementation. |
106 |
|
|
*/ |
107 |
|
|
void QCPPainter::setPen(Qt::PenStyle penStyle) { |
108 |
|
|
QPainter::setPen(penStyle); |
109 |
|
|
if (mModes.testFlag(pmNonCosmetic)) makeNonCosmetic(); |
110 |
|
|
} |
111 |
|
|
|
112 |
|
|
/*! \overload |
113 |
|
|
|
114 |
|
|
Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF |
115 |
|
|
unpredictable when antialiasing is disabled. Thus when antialiasing is |
116 |
|
|
disabled, it rounds the \a line to integer coordinates and then passes it to |
117 |
|
|
the original drawLine. |
118 |
|
|
|
119 |
|
|
\note this function hides the non-virtual base class implementation. |
120 |
|
|
*/ |
121 |
|
|
void QCPPainter::drawLine(const QLineF &line) { |
122 |
|
|
if (mIsAntialiasing || mModes.testFlag(pmVectorized)) |
123 |
|
|
QPainter::drawLine(line); |
124 |
|
|
else |
125 |
|
|
QPainter::drawLine(line.toLine()); |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
/*! |
129 |
|
|
Sets whether painting uses antialiasing or not. Use this method instead of |
130 |
|
|
using setRenderHint with QPainter::Antialiasing directly, as it allows |
131 |
|
|
QCPPainter to regain pixel exactness between antialiased and non-antialiased |
132 |
|
|
painting (Since Qt < 5.0 uses slightly different coordinate systems for |
133 |
|
|
AA/Non-AA painting). |
134 |
|
|
*/ |
135 |
|
|
void QCPPainter::setAntialiasing(bool enabled) { |
136 |
|
|
setRenderHint(QPainter::Antialiasing, enabled); |
137 |
|
|
if (mIsAntialiasing != enabled) { |
138 |
|
|
mIsAntialiasing = enabled; |
139 |
|
|
if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only |
140 |
|
|
// needed for rasterized outputs |
141 |
|
|
{ |
142 |
|
|
if (mIsAntialiasing) |
143 |
|
|
translate(0.5, 0.5); |
144 |
|
|
else |
145 |
|
|
translate(-0.5, -0.5); |
146 |
|
|
} |
147 |
|
|
} |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
/*! |
151 |
|
|
Sets the mode of the painter. This controls whether the painter shall adjust |
152 |
|
|
its fixes/workarounds optimized for certain output devices. |
153 |
|
|
*/ |
154 |
|
|
void QCPPainter::setModes(QCPPainter::PainterModes modes) { mModes = modes; } |
155 |
|
|
|
156 |
|
|
/*! |
157 |
|
|
Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after |
158 |
|
|
beginning painting on \a device. This is necessary to get cosmetic pen |
159 |
|
|
consistency across Qt versions, because since Qt5, all pens are non-cosmetic |
160 |
|
|
by default, and in Qt4 this render hint must be set to get that behaviour. |
161 |
|
|
|
162 |
|
|
The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts |
163 |
|
|
painting also sets the render hint as appropriate. |
164 |
|
|
|
165 |
|
|
\note this function hides the non-virtual base class implementation. |
166 |
|
|
*/ |
167 |
|
|
bool QCPPainter::begin(QPaintDevice *device) { |
168 |
|
|
bool result = QPainter::begin(device); |
169 |
|
|
#if QT_VERSION < \ |
170 |
|
|
QT_VERSION_CHECK(5, 0, \ |
171 |
|
|
0) // before Qt5, default pens used to be cosmetic if |
172 |
|
|
// NonCosmeticDefaultPen flag isn't set. So we set it |
173 |
|
|
// to get consistency across Qt versions. |
174 |
|
|
if (result) setRenderHint(QPainter::NonCosmeticDefaultPen); |
175 |
|
|
#endif |
176 |
|
|
return result; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
/*! \overload |
180 |
|
|
|
181 |
|
|
Sets the mode of the painter. This controls whether the painter shall adjust |
182 |
|
|
its fixes/workarounds optimized for certain output devices. |
183 |
|
|
*/ |
184 |
|
|
void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled) { |
185 |
|
|
if (!enabled && mModes.testFlag(mode)) |
186 |
|
|
mModes &= ~mode; |
187 |
|
|
else if (enabled && !mModes.testFlag(mode)) |
188 |
|
|
mModes |= mode; |
189 |
|
|
} |
190 |
|
|
|
191 |
|
|
/*! |
192 |
|
|
Saves the painter (see QPainter::save). Since QCPPainter adds some new |
193 |
|
|
internal state to QPainter, the save/restore functions are reimplemented to |
194 |
|
|
also save/restore those members. |
195 |
|
|
|
196 |
|
|
\note this function hides the non-virtual base class implementation. |
197 |
|
|
|
198 |
|
|
\see restore |
199 |
|
|
*/ |
200 |
|
|
void QCPPainter::save() { |
201 |
|
|
mAntialiasingStack.push(mIsAntialiasing); |
202 |
|
|
QPainter::save(); |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
/*! |
206 |
|
|
Restores the painter (see QPainter::restore). Since QCPPainter adds some new |
207 |
|
|
internal state to QPainter, the save/restore functions are reimplemented to |
208 |
|
|
also save/restore those members. |
209 |
|
|
|
210 |
|
|
\note this function hides the non-virtual base class implementation. |
211 |
|
|
|
212 |
|
|
\see save |
213 |
|
|
*/ |
214 |
|
|
void QCPPainter::restore() { |
215 |
|
|
if (!mAntialiasingStack.isEmpty()) |
216 |
|
|
mIsAntialiasing = mAntialiasingStack.pop(); |
217 |
|
|
else |
218 |
|
|
qDebug() << Q_FUNC_INFO << "Unbalanced save/restore"; |
219 |
|
|
QPainter::restore(); |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
/*! |
223 |
|
|
Changes the pen width to 1 if it currently is 0. This function is called in |
224 |
|
|
the \ref setPen overrides when the \ref pmNonCosmetic mode is set. |
225 |
|
|
*/ |
226 |
|
|
void QCPPainter::makeNonCosmetic() { |
227 |
|
|
if (qFuzzyIsNull(pen().widthF())) { |
228 |
|
|
QPen p = pen(); |
229 |
|
|
p.setWidth(1); |
230 |
|
|
QPainter::setPen(p); |
231 |
|
|
} |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
235 |
|
|
//////////////////// QCPScatterStyle |
236 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
237 |
|
|
|
238 |
|
|
/*! \class QCPScatterStyle |
239 |
|
|
\brief Represents the visual appearance of scatter points |
240 |
|
|
|
241 |
|
|
This class holds information about shape, color and size of scatter points. In |
242 |
|
|
plottables like QCPGraph it is used to store how scatter points shall be |
243 |
|
|
drawn. For example, \ref QCPGraph::setScatterStyle takes a QCPScatterStyle |
244 |
|
|
instance. |
245 |
|
|
|
246 |
|
|
A scatter style consists of a shape (\ref setShape), a line color (\ref |
247 |
|
|
setPen) and possibly a fill (\ref setBrush), if the shape provides a fillable |
248 |
|
|
area. Further, the size of the shape can be controlled with \ref setSize. |
249 |
|
|
|
250 |
|
|
\section QCPScatterStyle-defining Specifying a scatter style |
251 |
|
|
|
252 |
|
|
You can set all these configurations either by calling the respective |
253 |
|
|
functions on an instance: \snippet |
254 |
|
|
documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1 |
255 |
|
|
|
256 |
|
|
Or you can use one of the various constructors that take different parameter |
257 |
|
|
combinations, making it easy to specify a scatter style in a single call, like |
258 |
|
|
so: \snippet documentation/doc-code-snippets/mainwindow.cpp |
259 |
|
|
qcpscatterstyle-creation-2 |
260 |
|
|
|
261 |
|
|
\section QCPScatterStyle-undefinedpen Leaving the color/pen up to the |
262 |
|
|
plottable |
263 |
|
|
|
264 |
|
|
There are two constructors which leave the pen undefined: \ref |
265 |
|
|
QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). |
266 |
|
|
If those constructors are used, a call to \ref isPenDefined will return false. |
267 |
|
|
It leads to scatter points that inherit the pen from the plottable that uses |
268 |
|
|
the scatter style. Thus, if such a scatter style is passed to QCPGraph, the |
269 |
|
|
line color of the graph (\ref QCPGraph::setPen) will be used by the scatter |
270 |
|
|
points. This makes it very convenient to set up typical scatter settings: |
271 |
|
|
|
272 |
|
|
\snippet documentation/doc-code-snippets/mainwindow.cpp |
273 |
|
|
qcpscatterstyle-shortcreation |
274 |
|
|
|
275 |
|
|
Notice that it wasn't even necessary to explicitly call a QCPScatterStyle |
276 |
|
|
constructor. This works because QCPScatterStyle provides a constructor that |
277 |
|
|
can transform a \ref ScatterShape directly into a QCPScatterStyle instance |
278 |
|
|
(that's the \ref QCPScatterStyle(ScatterShape shape, double size) constructor |
279 |
|
|
with a default for \a size). In those cases, C++ allows directly supplying a |
280 |
|
|
\ref ScatterShape, where actually a QCPScatterStyle is expected. |
281 |
|
|
|
282 |
|
|
\section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps |
283 |
|
|
|
284 |
|
|
QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as |
285 |
|
|
scatter points. |
286 |
|
|
|
287 |
|
|
For custom shapes, you can provide a QPainterPath with the desired shape to |
288 |
|
|
the \ref setCustomPath function or call the constructor that takes a painter |
289 |
|
|
path. The scatter shape will automatically be set to \ref ssCustom. |
290 |
|
|
|
291 |
|
|
For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively |
292 |
|
|
you can use the constructor that takes a QPixmap. The scatter shape will |
293 |
|
|
automatically be set to \ref ssPixmap. Note that \ref setSize does not |
294 |
|
|
influence the appearance of the pixmap. |
295 |
|
|
*/ |
296 |
|
|
|
297 |
|
|
/* start documentation of inline functions */ |
298 |
|
|
|
299 |
|
|
/*! \fn bool QCPScatterStyle::isNone() const |
300 |
|
|
|
301 |
|
|
Returns whether the scatter shape is \ref ssNone. |
302 |
|
|
|
303 |
|
|
\see setShape |
304 |
|
|
*/ |
305 |
|
|
|
306 |
|
|
/*! \fn bool QCPScatterStyle::isPenDefined() const |
307 |
|
|
|
308 |
|
|
Returns whether a pen has been defined for this scatter style. |
309 |
|
|
|
310 |
|
|
The pen is undefined if a constructor is called that does not carry \a pen as |
311 |
|
|
parameter. Those are \ref QCPScatterStyle() and \ref |
312 |
|
|
QCPScatterStyle(ScatterShape shape, double size). If the pen is left |
313 |
|
|
undefined, the scatter color will be inherited from the plottable that uses |
314 |
|
|
this scatter style. |
315 |
|
|
|
316 |
|
|
\see setPen |
317 |
|
|
*/ |
318 |
|
|
|
319 |
|
|
/* end documentation of inline functions */ |
320 |
|
|
|
321 |
|
|
/*! |
322 |
|
|
Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or |
323 |
|
|
brush is defined. |
324 |
|
|
|
325 |
|
|
Since the pen is undefined (\ref isPenDefined returns false), the scatter |
326 |
|
|
color will be inherited from the plottable that uses this scatter style. |
327 |
|
|
*/ |
328 |
|
|
QCPScatterStyle::QCPScatterStyle() |
329 |
|
|
: mSize(6), |
330 |
|
|
mShape(ssNone), |
331 |
|
|
mPen(Qt::NoPen), |
332 |
|
|
mBrush(Qt::NoBrush), |
333 |
|
|
mPenDefined(false) {} |
334 |
|
|
|
335 |
|
|
/*! |
336 |
|
|
Creates a new QCPScatterStyle instance with shape set to \a shape and size to |
337 |
|
|
\a size. No pen or brush is defined. |
338 |
|
|
|
339 |
|
|
Since the pen is undefined (\ref isPenDefined returns false), the scatter |
340 |
|
|
color will be inherited from the plottable that uses this scatter style. |
341 |
|
|
*/ |
342 |
|
|
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) |
343 |
|
|
: mSize(size), |
344 |
|
|
mShape(shape), |
345 |
|
|
mPen(Qt::NoPen), |
346 |
|
|
mBrush(Qt::NoBrush), |
347 |
|
|
mPenDefined(false) {} |
348 |
|
|
|
349 |
|
|
/*! |
350 |
|
|
Creates a new QCPScatterStyle instance with shape set to \a shape, the pen |
351 |
|
|
color set to \a color, and size to \a size. No brush is defined, i.e. the |
352 |
|
|
scatter point will not be filled. |
353 |
|
|
*/ |
354 |
|
|
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, |
355 |
|
|
double size) |
356 |
|
|
: mSize(size), |
357 |
|
|
mShape(shape), |
358 |
|
|
mPen(QPen(color)), |
359 |
|
|
mBrush(Qt::NoBrush), |
360 |
|
|
mPenDefined(true) {} |
361 |
|
|
|
362 |
|
|
/*! |
363 |
|
|
Creates a new QCPScatterStyle instance with shape set to \a shape, the pen |
364 |
|
|
color set to \a color, the brush color to \a fill (with a solid pattern), and |
365 |
|
|
size to \a size. |
366 |
|
|
*/ |
367 |
|
|
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, |
368 |
|
|
const QColor &fill, double size) |
369 |
|
|
: mSize(size), |
370 |
|
|
mShape(shape), |
371 |
|
|
mPen(QPen(color)), |
372 |
|
|
mBrush(QBrush(fill)), |
373 |
|
|
mPenDefined(true) {} |
374 |
|
|
|
375 |
|
|
/*! |
376 |
|
|
Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set |
377 |
|
|
to \a pen, the brush to \a brush, and size to \a size. |
378 |
|
|
|
379 |
|
|
\warning In some cases it might be tempting to directly use a pen style like |
380 |
|
|
<tt>Qt::NoPen</tt> as \a pen and a color like <tt>Qt::blue</tt> as \a brush. |
381 |
|
|
Notice however, that the corresponding call\n |
382 |
|
|
<tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n |
383 |
|
|
doesn't necessarily lead C++ to use this constructor in some cases, but might |
384 |
|
|
mistake <tt>Qt::NoPen</tt> for a QColor and use the \ref |
385 |
|
|
QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, |
386 |
|
|
double size) constructor instead (which will lead to an unexpected look of the |
387 |
|
|
scatter points). To prevent this, be more explicit with the parameter types. |
388 |
|
|
For example, use <tt>QBrush(Qt::blue)</tt> instead of just <tt>Qt::blue</tt>, |
389 |
|
|
to clearly point out to the compiler that this constructor is wanted. |
390 |
|
|
*/ |
391 |
|
|
QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, |
392 |
|
|
const QBrush &brush, double size) |
393 |
|
|
: mSize(size), |
394 |
|
|
mShape(shape), |
395 |
|
|
mPen(pen), |
396 |
|
|
mBrush(brush), |
397 |
|
|
mPenDefined(pen.style() != Qt::NoPen) {} |
398 |
|
|
|
399 |
|
|
/*! |
400 |
|
|
Creates a new QCPScatterStyle instance which will show the specified \a |
401 |
|
|
pixmap. The scatter shape is set to \ref ssPixmap. |
402 |
|
|
*/ |
403 |
|
|
QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) |
404 |
|
|
: mSize(5), |
405 |
|
|
mShape(ssPixmap), |
406 |
|
|
mPen(Qt::NoPen), |
407 |
|
|
mBrush(Qt::NoBrush), |
408 |
|
|
mPixmap(pixmap), |
409 |
|
|
mPenDefined(false) {} |
410 |
|
|
|
411 |
|
|
/*! |
412 |
|
|
Creates a new QCPScatterStyle instance with a custom shape that is defined via |
413 |
|
|
\a customPath. The scatter shape is set to \ref ssCustom. |
414 |
|
|
|
415 |
|
|
The custom shape line will be drawn with \a pen and filled with \a brush. The |
416 |
|
|
size has a slightly different meaning than for built-in scatter points: The |
417 |
|
|
custom path will be drawn scaled by a factor of \a size/6.0. Since the default |
418 |
|
|
\a size is 6, the custom path will appear at a its natural size by default. To |
419 |
|
|
double the size of the path for example, set \a size to 12. |
420 |
|
|
*/ |
421 |
|
|
QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, |
422 |
|
|
const QPen &pen, const QBrush &brush, |
423 |
|
|
double size) |
424 |
|
|
: mSize(size), |
425 |
|
|
mShape(ssCustom), |
426 |
|
|
mPen(pen), |
427 |
|
|
mBrush(brush), |
428 |
|
|
mCustomPath(customPath), |
429 |
|
|
mPenDefined(pen.style() != Qt::NoPen) {} |
430 |
|
|
|
431 |
|
|
/*! |
432 |
|
|
Sets the size (pixel diameter) of the drawn scatter points to \a size. |
433 |
|
|
|
434 |
|
|
\see setShape |
435 |
|
|
*/ |
436 |
|
|
void QCPScatterStyle::setSize(double size) { mSize = size; } |
437 |
|
|
|
438 |
|
|
/*! |
439 |
|
|
Sets the shape to \a shape. |
440 |
|
|
|
441 |
|
|
Note that the calls \ref setPixmap and \ref setCustomPath automatically set |
442 |
|
|
the shape to \ref ssPixmap and \ref ssCustom, respectively. |
443 |
|
|
|
444 |
|
|
\see setSize |
445 |
|
|
*/ |
446 |
|
|
void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape) { |
447 |
|
|
mShape = shape; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
/*! |
451 |
|
|
Sets the pen that will be used to draw scatter points to \a pen. |
452 |
|
|
|
453 |
|
|
If the pen was previously undefined (see \ref isPenDefined), the pen is |
454 |
|
|
considered defined after a call to this function, even if \a pen is |
455 |
|
|
<tt>Qt::NoPen</tt>. |
456 |
|
|
|
457 |
|
|
\see setBrush |
458 |
|
|
*/ |
459 |
|
|
void QCPScatterStyle::setPen(const QPen &pen) { |
460 |
|
|
mPenDefined = true; |
461 |
|
|
mPen = pen; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
/*! |
465 |
|
|
Sets the brush that will be used to fill scatter points to \a brush. Note that |
466 |
|
|
not all scatter shapes have fillable areas. For example, \ref ssPlus does not |
467 |
|
|
while \ref ssCircle does. |
468 |
|
|
|
469 |
|
|
\see setPen |
470 |
|
|
*/ |
471 |
|
|
void QCPScatterStyle::setBrush(const QBrush &brush) { mBrush = brush; } |
472 |
|
|
|
473 |
|
|
/*! |
474 |
|
|
Sets the pixmap that will be drawn as scatter point to \a pixmap. |
475 |
|
|
|
476 |
|
|
Note that \ref setSize does not influence the appearance of the pixmap. |
477 |
|
|
|
478 |
|
|
The scatter shape is automatically set to \ref ssPixmap. |
479 |
|
|
*/ |
480 |
|
|
void QCPScatterStyle::setPixmap(const QPixmap &pixmap) { |
481 |
|
|
setShape(ssPixmap); |
482 |
|
|
mPixmap = pixmap; |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
/*! |
486 |
|
|
Sets the custom shape that will be drawn as scatter point to \a customPath. |
487 |
|
|
|
488 |
|
|
The scatter shape is automatically set to \ref ssCustom. |
489 |
|
|
*/ |
490 |
|
|
void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) { |
491 |
|
|
setShape(ssCustom); |
492 |
|
|
mCustomPath = customPath; |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
/*! |
496 |
|
|
Applies the pen and the brush of this scatter style to \a painter. If this |
497 |
|
|
scatter style has an undefined pen (\ref isPenDefined), sets the pen of \a |
498 |
|
|
painter to \a defaultPen instead. |
499 |
|
|
|
500 |
|
|
This function is used by plottables (or any class that wants to draw scatters) |
501 |
|
|
just before a number of scatters with this style shall be drawn with the \a |
502 |
|
|
painter. |
503 |
|
|
|
504 |
|
|
\see drawShape |
505 |
|
|
*/ |
506 |
|
|
void QCPScatterStyle::applyTo(QCPPainter *painter, |
507 |
|
|
const QPen &defaultPen) const { |
508 |
|
|
painter->setPen(mPenDefined ? mPen : defaultPen); |
509 |
|
|
painter->setBrush(mBrush); |
510 |
|
|
} |
511 |
|
|
|
512 |
|
|
/*! |
513 |
|
|
Draws the scatter shape with \a painter at position \a pos. |
514 |
|
|
|
515 |
|
|
This function does not modify the pen or the brush on the painter, as \ref |
516 |
|
|
applyTo is meant to be called before scatter points are drawn with \ref |
517 |
|
|
drawShape. |
518 |
|
|
|
519 |
|
|
\see applyTo |
520 |
|
|
*/ |
521 |
|
|
void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const { |
522 |
|
|
drawShape(painter, pos.x(), pos.y()); |
523 |
|
|
} |
524 |
|
|
|
525 |
|
|
/*! \overload |
526 |
|
|
Draws the scatter shape with \a painter at position \a x and \a y. |
527 |
|
|
*/ |
528 |
|
|
void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const { |
529 |
|
|
double w = mSize / 2.0; |
530 |
|
|
switch (mShape) { |
531 |
|
|
case ssNone: |
532 |
|
|
break; |
533 |
|
|
case ssDot: { |
534 |
|
|
painter->drawLine(QPointF(x, y), QPointF(x + 0.0001, y)); |
535 |
|
|
break; |
536 |
|
|
} |
537 |
|
|
case ssCross: { |
538 |
|
|
painter->drawLine(QLineF(x - w, y - w, x + w, y + w)); |
539 |
|
|
painter->drawLine(QLineF(x - w, y + w, x + w, y - w)); |
540 |
|
|
break; |
541 |
|
|
} |
542 |
|
|
case ssPlus: { |
543 |
|
|
painter->drawLine(QLineF(x - w, y, x + w, y)); |
544 |
|
|
painter->drawLine(QLineF(x, y + w, x, y - w)); |
545 |
|
|
break; |
546 |
|
|
} |
547 |
|
|
case ssCircle: { |
548 |
|
|
painter->drawEllipse(QPointF(x, y), w, w); |
549 |
|
|
break; |
550 |
|
|
} |
551 |
|
|
case ssDisc: { |
552 |
|
|
QBrush b = painter->brush(); |
553 |
|
|
painter->setBrush(painter->pen().color()); |
554 |
|
|
painter->drawEllipse(QPointF(x, y), w, w); |
555 |
|
|
painter->setBrush(b); |
556 |
|
|
break; |
557 |
|
|
} |
558 |
|
|
case ssSquare: { |
559 |
|
|
painter->drawRect(QRectF(x - w, y - w, mSize, mSize)); |
560 |
|
|
break; |
561 |
|
|
} |
562 |
|
|
case ssDiamond: { |
563 |
|
|
painter->drawLine(QLineF(x - w, y, x, y - w)); |
564 |
|
|
painter->drawLine(QLineF(x, y - w, x + w, y)); |
565 |
|
|
painter->drawLine(QLineF(x + w, y, x, y + w)); |
566 |
|
|
painter->drawLine(QLineF(x, y + w, x - w, y)); |
567 |
|
|
break; |
568 |
|
|
} |
569 |
|
|
case ssStar: { |
570 |
|
|
painter->drawLine(QLineF(x - w, y, x + w, y)); |
571 |
|
|
painter->drawLine(QLineF(x, y + w, x, y - w)); |
572 |
|
|
painter->drawLine( |
573 |
|
|
QLineF(x - w * 0.707, y - w * 0.707, x + w * 0.707, y + w * 0.707)); |
574 |
|
|
painter->drawLine( |
575 |
|
|
QLineF(x - w * 0.707, y + w * 0.707, x + w * 0.707, y - w * 0.707)); |
576 |
|
|
break; |
577 |
|
|
} |
578 |
|
|
case ssTriangle: { |
579 |
|
|
painter->drawLine(QLineF(x - w, y + 0.755 * w, x + w, y + 0.755 * w)); |
580 |
|
|
painter->drawLine(QLineF(x + w, y + 0.755 * w, x, y - 0.977 * w)); |
581 |
|
|
painter->drawLine(QLineF(x, y - 0.977 * w, x - w, y + 0.755 * w)); |
582 |
|
|
break; |
583 |
|
|
} |
584 |
|
|
case ssTriangleInverted: { |
585 |
|
|
painter->drawLine(QLineF(x - w, y - 0.755 * w, x + w, y - 0.755 * w)); |
586 |
|
|
painter->drawLine(QLineF(x + w, y - 0.755 * w, x, y + 0.977 * w)); |
587 |
|
|
painter->drawLine(QLineF(x, y + 0.977 * w, x - w, y - 0.755 * w)); |
588 |
|
|
break; |
589 |
|
|
} |
590 |
|
|
case ssCrossSquare: { |
591 |
|
|
painter->drawLine(QLineF(x - w, y - w, x + w * 0.95, y + w * 0.95)); |
592 |
|
|
painter->drawLine(QLineF(x - w, y + w * 0.95, x + w * 0.95, y - w)); |
593 |
|
|
painter->drawRect(QRectF(x - w, y - w, mSize, mSize)); |
594 |
|
|
break; |
595 |
|
|
} |
596 |
|
|
case ssPlusSquare: { |
597 |
|
|
painter->drawLine(QLineF(x - w, y, x + w * 0.95, y)); |
598 |
|
|
painter->drawLine(QLineF(x, y + w, x, y - w)); |
599 |
|
|
painter->drawRect(QRectF(x - w, y - w, mSize, mSize)); |
600 |
|
|
break; |
601 |
|
|
} |
602 |
|
|
case ssCrossCircle: { |
603 |
|
|
painter->drawLine( |
604 |
|
|
QLineF(x - w * 0.707, y - w * 0.707, x + w * 0.670, y + w * 0.670)); |
605 |
|
|
painter->drawLine( |
606 |
|
|
QLineF(x - w * 0.707, y + w * 0.670, x + w * 0.670, y - w * 0.707)); |
607 |
|
|
painter->drawEllipse(QPointF(x, y), w, w); |
608 |
|
|
break; |
609 |
|
|
} |
610 |
|
|
case ssPlusCircle: { |
611 |
|
|
painter->drawLine(QLineF(x - w, y, x + w, y)); |
612 |
|
|
painter->drawLine(QLineF(x, y + w, x, y - w)); |
613 |
|
|
painter->drawEllipse(QPointF(x, y), w, w); |
614 |
|
|
break; |
615 |
|
|
} |
616 |
|
|
case ssPeace: { |
617 |
|
|
painter->drawLine(QLineF(x, y - w, x, y + w)); |
618 |
|
|
painter->drawLine(QLineF(x, y, x - w * 0.707, y + w * 0.707)); |
619 |
|
|
painter->drawLine(QLineF(x, y, x + w * 0.707, y + w * 0.707)); |
620 |
|
|
painter->drawEllipse(QPointF(x, y), w, w); |
621 |
|
|
break; |
622 |
|
|
} |
623 |
|
|
case ssPixmap: { |
624 |
|
|
painter->drawPixmap(x - mPixmap.width() * 0.5, y - mPixmap.height() * 0.5, |
625 |
|
|
mPixmap); |
626 |
|
|
break; |
627 |
|
|
} |
628 |
|
|
case ssCustom: { |
629 |
|
|
QTransform oldTransform = painter->transform(); |
630 |
|
|
painter->translate(x, y); |
631 |
|
|
painter->scale(mSize / 6.0, mSize / 6.0); |
632 |
|
|
painter->drawPath(mCustomPath); |
633 |
|
|
painter->setTransform(oldTransform); |
634 |
|
|
break; |
635 |
|
|
} |
636 |
|
|
} |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
640 |
|
|
//////////////////// QCPLayer |
641 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
642 |
|
|
|
643 |
|
|
/*! \class QCPLayer |
644 |
|
|
\brief A layer that may contain objects, to control the rendering order |
645 |
|
|
|
646 |
|
|
The Layering system of QCustomPlot is the mechanism to control the rendering |
647 |
|
|
order of the elements inside the plot. |
648 |
|
|
|
649 |
|
|
It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an |
650 |
|
|
ordered list of one or more instances of QCPLayer (see QCustomPlot::addLayer, |
651 |
|
|
QCustomPlot::layer, QCustomPlot::moveLayer, etc.). When replotting, |
652 |
|
|
QCustomPlot goes through the list of layers bottom to top and successively |
653 |
|
|
draws the layerables of the layers. |
654 |
|
|
|
655 |
|
|
A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is |
656 |
|
|
an abstract base class from which almost all visible objects derive, like |
657 |
|
|
axes, grids, graphs, items, etc. |
658 |
|
|
|
659 |
|
|
Initially, QCustomPlot has five layers: "background", "grid", "main", "axes" |
660 |
|
|
and "legend" (in that order). The top two layers "axes" and "legend" contain |
661 |
|
|
the default axes and legend, so they will be drawn on top. In the middle, |
662 |
|
|
there is the "main" layer. It is initially empty and set as the current layer |
663 |
|
|
(see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc. |
664 |
|
|
are created on this layer by default. Then comes the "grid" layer which |
665 |
|
|
contains the QCPGrid instances (which belong tightly to QCPAxis, see \ref |
666 |
|
|
QCPAxis::grid). The Axis rect background shall be drawn behind everything |
667 |
|
|
else, thus the default QCPAxisRect instance is placed on the "background" |
668 |
|
|
layer. Of course, the layer affiliation of the individual objects can be |
669 |
|
|
changed as required (\ref QCPLayerable::setLayer). |
670 |
|
|
|
671 |
|
|
Controlling the ordering of objects is easy: Create a new layer in the |
672 |
|
|
position you want it to be, e.g. above "main", with QCustomPlot::addLayer. |
673 |
|
|
Then set the current layer with QCustomPlot::setCurrentLayer to that new layer |
674 |
|
|
and finally create the objects normally. They will be placed on the new layer |
675 |
|
|
automatically, due to the current layer setting. Alternatively you could have |
676 |
|
|
also ignored the current layer setting and just moved the objects with |
677 |
|
|
QCPLayerable::setLayer to the desired layer after creating them. |
678 |
|
|
|
679 |
|
|
It is also possible to move whole layers. For example, If you want the grid to |
680 |
|
|
be shown in front of all plottables/items on the "main" layer, just move it |
681 |
|
|
above "main" with QCustomPlot::moveLayer. |
682 |
|
|
|
683 |
|
|
The rendering order within one layer is simply by order of creation or |
684 |
|
|
insertion. The item created last (or added last to the layer), is drawn on top |
685 |
|
|
of all other objects on that layer. |
686 |
|
|
|
687 |
|
|
When a layer is deleted, the objects on it are not deleted with it, but fall |
688 |
|
|
on the layer below the deleted layer, see QCustomPlot::removeLayer. |
689 |
|
|
*/ |
690 |
|
|
|
691 |
|
|
/* start documentation of inline functions */ |
692 |
|
|
|
693 |
|
|
/*! \fn QList<QCPLayerable*> QCPLayer::children() const |
694 |
|
|
|
695 |
|
|
Returns a list of all layerables on this layer. The order corresponds to the |
696 |
|
|
rendering order: layerables with higher indices are drawn above layerables |
697 |
|
|
with lower indices. |
698 |
|
|
*/ |
699 |
|
|
|
700 |
|
|
/*! \fn int QCPLayer::index() const |
701 |
|
|
|
702 |
|
|
Returns the index this layer has in the QCustomPlot. The index is the integer |
703 |
|
|
number by which this layer can be accessed via \ref QCustomPlot::layer. |
704 |
|
|
|
705 |
|
|
Layers with higher indices will be drawn above layers with lower indices. |
706 |
|
|
*/ |
707 |
|
|
|
708 |
|
|
/* end documentation of inline functions */ |
709 |
|
|
|
710 |
|
|
/*! |
711 |
|
|
Creates a new QCPLayer instance. |
712 |
|
|
|
713 |
|
|
Normally you shouldn't directly instantiate layers, use \ref |
714 |
|
|
QCustomPlot::addLayer instead. |
715 |
|
|
|
716 |
|
|
\warning It is not checked that \a layerName is actually a unique layer name |
717 |
|
|
in \a parentPlot. This check is only performed by \ref QCustomPlot::addLayer. |
718 |
|
|
*/ |
719 |
|
|
QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) |
720 |
|
|
: QObject(parentPlot), |
721 |
|
|
mParentPlot(parentPlot), |
722 |
|
|
mName(layerName), |
723 |
|
|
mIndex(-1), // will be set to a proper value by the QCustomPlot layer |
724 |
|
|
// creation function |
725 |
|
|
mVisible(true) { |
726 |
|
|
// Note: no need to make sure layerName is unique, because layer |
727 |
|
|
// management is done with QCustomPlot functions. |
728 |
|
|
} |
729 |
|
|
|
730 |
|
|
QCPLayer::~QCPLayer() { |
731 |
|
|
// If child layerables are still on this layer, detach them, so they don't try |
732 |
|
|
// to reach back to this then invalid layer once they get deleted/moved |
733 |
|
|
// themselves. This only happens when layers are deleted directly, like in the |
734 |
|
|
// QCustomPlot destructor. (The regular layer removal procedure for the user |
735 |
|
|
// is to call QCustomPlot::removeLayer, which moves all layerables off this |
736 |
|
|
// layer before deleting it.) |
737 |
|
|
|
738 |
|
|
while (!mChildren.isEmpty()) |
739 |
|
|
mChildren.last()->setLayer( |
740 |
|
|
0); // removes itself from mChildren via removeChild() |
741 |
|
|
|
742 |
|
|
if (mParentPlot->currentLayer() == this) |
743 |
|
|
qDebug() << Q_FUNC_INFO |
744 |
|
|
<< "The parent plot's mCurrentLayer will be a dangling pointer. " |
745 |
|
|
"Should have been set to a valid layer or 0 beforehand."; |
746 |
|
|
} |
747 |
|
|
|
748 |
|
|
/*! |
749 |
|
|
Sets whether this layer is visible or not. If \a visible is set to false, all |
750 |
|
|
layerables on this layer will be invisible. |
751 |
|
|
|
752 |
|
|
This function doesn't change the visibility property of the layerables (\ref |
753 |
|
|
QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each |
754 |
|
|
layerable takes the visibility of the parent layer into account. |
755 |
|
|
*/ |
756 |
|
|
void QCPLayer::setVisible(bool visible) { mVisible = visible; } |
757 |
|
|
|
758 |
|
|
/*! \internal |
759 |
|
|
|
760 |
|
|
Adds the \a layerable to the list of this layer. If \a prepend is set to true, |
761 |
|
|
the layerable will be prepended to the list, i.e. be drawn beneath the other |
762 |
|
|
layerables already in the list. |
763 |
|
|
|
764 |
|
|
This function does not change the \a mLayer member of \a layerable to this |
765 |
|
|
layer. (Use QCPLayerable::setLayer to change the layer of an object, not this |
766 |
|
|
function.) |
767 |
|
|
|
768 |
|
|
\see removeChild |
769 |
|
|
*/ |
770 |
|
|
void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) { |
771 |
|
|
if (!mChildren.contains(layerable)) { |
772 |
|
|
if (prepend) |
773 |
|
|
mChildren.prepend(layerable); |
774 |
|
|
else |
775 |
|
|
mChildren.append(layerable); |
776 |
|
|
} else |
777 |
|
|
qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" |
778 |
|
|
<< reinterpret_cast<quintptr>(layerable); |
779 |
|
|
} |
780 |
|
|
|
781 |
|
|
/*! \internal |
782 |
|
|
|
783 |
|
|
Removes the \a layerable from the list of this layer. |
784 |
|
|
|
785 |
|
|
This function does not change the \a mLayer member of \a layerable. (Use |
786 |
|
|
QCPLayerable::setLayer to change the layer of an object, not this function.) |
787 |
|
|
|
788 |
|
|
\see addChild |
789 |
|
|
*/ |
790 |
|
|
void QCPLayer::removeChild(QCPLayerable *layerable) { |
791 |
|
|
if (!mChildren.removeOne(layerable)) |
792 |
|
|
qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" |
793 |
|
|
<< reinterpret_cast<quintptr>(layerable); |
794 |
|
|
} |
795 |
|
|
|
796 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
797 |
|
|
//////////////////// QCPLayerable |
798 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
799 |
|
|
|
800 |
|
|
/*! \class QCPLayerable |
801 |
|
|
\brief Base class for all drawable objects |
802 |
|
|
|
803 |
|
|
This is the abstract base class most visible objects derive from, e.g. |
804 |
|
|
plottables, axes, grid etc. |
805 |
|
|
|
806 |
|
|
Every layerable is on a layer (QCPLayer) which allows controlling the |
807 |
|
|
rendering order by stacking the layers accordingly. |
808 |
|
|
|
809 |
|
|
For details about the layering mechanism, see the QCPLayer documentation. |
810 |
|
|
*/ |
811 |
|
|
|
812 |
|
|
/* start documentation of inline functions */ |
813 |
|
|
|
814 |
|
|
/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const |
815 |
|
|
|
816 |
|
|
Returns the parent layerable of this layerable. The parent layerable is used |
817 |
|
|
to provide visibility hierarchies in conjunction with the method \ref |
818 |
|
|
realVisibility. This way, layerables only get drawn if their parent layerables |
819 |
|
|
are visible, too. |
820 |
|
|
|
821 |
|
|
Note that a parent layerable is not necessarily also the QObject parent for |
822 |
|
|
memory management. Further, a layerable doesn't always have a parent |
823 |
|
|
layerable, so this function may return 0. |
824 |
|
|
|
825 |
|
|
A parent layerable is set implicitly with when placed inside layout elements |
826 |
|
|
and doesn't need to be set manually by the user. |
827 |
|
|
*/ |
828 |
|
|
|
829 |
|
|
/* end documentation of inline functions */ |
830 |
|
|
/* start documentation of pure virtual functions */ |
831 |
|
|
|
832 |
|
|
/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter |
833 |
|
|
*painter) const = 0 \internal |
834 |
|
|
|
835 |
|
|
This function applies the default antialiasing setting to the specified \a |
836 |
|
|
painter, using the function \ref applyAntialiasingHint. It is the antialiasing |
837 |
|
|
state the painter is put in, when \ref draw is called on the layerable. If the |
838 |
|
|
layerable has multiple entities whose antialiasing setting may be specified |
839 |
|
|
individually, this function should set the antialiasing state of the most |
840 |
|
|
prominent entity. In this case however, the \ref draw function usually calls |
841 |
|
|
the specialized versions of this function before drawing each entity, |
842 |
|
|
effectively overriding the setting of the default antialiasing hint. |
843 |
|
|
|
844 |
|
|
<b>First example:</b> QCPGraph has multiple entities that have an antialiasing |
845 |
|
|
setting: The graph line, fills, scatters and error bars. Those can be |
846 |
|
|
configured via QCPGraph::setAntialiased, QCPGraph::setAntialiasedFill, |
847 |
|
|
QCPGraph::setAntialiasedScatters etc. Consequently, there isn't only the |
848 |
|
|
QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the |
849 |
|
|
graph line's antialiasing), but specialized ones like |
850 |
|
|
QCPGraph::applyFillAntialiasingHint and |
851 |
|
|
QCPGraph::applyScattersAntialiasingHint. So before drawing one of those |
852 |
|
|
entities, QCPGraph::draw calls the respective specialized |
853 |
|
|
applyAntialiasingHint function. |
854 |
|
|
|
855 |
|
|
<b>Second example:</b> QCPItemLine consists only of a line so there is only |
856 |
|
|
one antialiasing setting which can be controlled with |
857 |
|
|
QCPItemLine::setAntialiased. (This function is inherited by all layerables. |
858 |
|
|
The specialized functions, as seen on QCPGraph, must be added explicitly to |
859 |
|
|
the respective layerable subclass.) Consequently it only has the normal |
860 |
|
|
QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function |
861 |
|
|
doesn't need to care about setting any antialiasing states, because the |
862 |
|
|
default antialiasing hint is already set on the painter when the \ref draw |
863 |
|
|
function is called, and that's the state it wants to draw the line with. |
864 |
|
|
*/ |
865 |
|
|
|
866 |
|
|
/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0 |
867 |
|
|
\internal |
868 |
|
|
|
869 |
|
|
This function draws the layerable with the specified \a painter. It is only |
870 |
|
|
called by QCustomPlot, if the layerable is visible (\ref setVisible). |
871 |
|
|
|
872 |
|
|
Before this function is called, the painter's antialiasing state is set via |
873 |
|
|
\ref applyDefaultAntialiasingHint, see the documentation there. Further, the |
874 |
|
|
clipping rectangle was set to \ref clipRect. |
875 |
|
|
*/ |
876 |
|
|
|
877 |
|
|
/* end documentation of pure virtual functions */ |
878 |
|
|
/* start documentation of signals */ |
879 |
|
|
|
880 |
|
|
/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer); |
881 |
|
|
|
882 |
|
|
This signal is emitted when the layer of this layerable changes, i.e. this |
883 |
|
|
layerable is moved to a different layer. |
884 |
|
|
|
885 |
|
|
\see setLayer |
886 |
|
|
*/ |
887 |
|
|
|
888 |
|
|
/* end documentation of signals */ |
889 |
|
|
|
890 |
|
|
/*! |
891 |
|
|
Creates a new QCPLayerable instance. |
892 |
|
|
|
893 |
|
|
Since QCPLayerable is an abstract base class, it can't be instantiated |
894 |
|
|
directly. Use one of the derived classes. |
895 |
|
|
|
896 |
|
|
If \a plot is provided, it automatically places itself on the layer named \a |
897 |
|
|
targetLayer. If \a targetLayer is an empty string, it places itself on the |
898 |
|
|
current layer of the plot (see \ref QCustomPlot::setCurrentLayer). |
899 |
|
|
|
900 |
|
|
It is possible to provide 0 as \a plot. In that case, you should assign a |
901 |
|
|
parent plot at a later time with \ref initializeParentPlot. |
902 |
|
|
|
903 |
|
|
The layerable's parent layerable is set to \a parentLayerable, if provided. |
904 |
|
|
Direct layerable parents are mainly used to control visibility in a hierarchy |
905 |
|
|
of layerables. This means a layerable is only drawn, if all its ancestor |
906 |
|
|
layerables are also visible. Note that \a parentLayerable does not become the |
907 |
|
|
QObject-parent (for memory management) of this layerable, \a plot does. It is |
908 |
|
|
not uncommon to set the QObject-parent to something else in the constructors |
909 |
|
|
of QCPLayerable subclasses, to guarantee a working destruction hierarchy. |
910 |
|
|
*/ |
911 |
|
|
QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, |
912 |
|
|
QCPLayerable *parentLayerable) |
913 |
|
|
: QObject(plot), |
914 |
|
|
mVisible(true), |
915 |
|
|
mParentPlot(plot), |
916 |
|
|
mParentLayerable(parentLayerable), |
917 |
|
|
mLayer(0), |
918 |
|
|
mAntialiased(true) { |
919 |
|
|
if (mParentPlot) { |
920 |
|
|
if (targetLayer.isEmpty()) |
921 |
|
|
setLayer(mParentPlot->currentLayer()); |
922 |
|
|
else if (!setLayer(targetLayer)) |
923 |
|
|
qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" |
924 |
|
|
<< targetLayer << "failed."; |
925 |
|
|
} |
926 |
|
|
} |
927 |
|
|
|
928 |
|
|
QCPLayerable::~QCPLayerable() { |
929 |
|
|
if (mLayer) { |
930 |
|
|
mLayer->removeChild(this); |
931 |
|
|
mLayer = 0; |
932 |
|
|
} |
933 |
|
|
} |
934 |
|
|
|
935 |
|
|
/*! |
936 |
|
|
Sets the visibility of this layerable object. If an object is not visible, it |
937 |
|
|
will not be drawn on the QCustomPlot surface, and user interaction with it |
938 |
|
|
(e.g. click and selection) is not possible. |
939 |
|
|
*/ |
940 |
|
|
void QCPLayerable::setVisible(bool on) { mVisible = on; } |
941 |
|
|
|
942 |
|
|
/*! |
943 |
|
|
Sets the \a layer of this layerable object. The object will be placed on top |
944 |
|
|
of the other objects already on \a layer. |
945 |
|
|
|
946 |
|
|
If \a layer is 0, this layerable will not be on any layer and thus not appear |
947 |
|
|
in the plot (or interact/receive events). |
948 |
|
|
|
949 |
|
|
Returns true if the layer of this layerable was successfully changed to \a |
950 |
|
|
layer. |
951 |
|
|
*/ |
952 |
|
|
bool QCPLayerable::setLayer(QCPLayer *layer) { |
953 |
|
|
return moveToLayer(layer, false); |
954 |
|
|
} |
955 |
|
|
|
956 |
|
|
/*! \overload |
957 |
|
|
Sets the layer of this layerable object by name |
958 |
|
|
|
959 |
|
|
Returns true on success, i.e. if \a layerName is a valid layer name. |
960 |
|
|
*/ |
961 |
|
|
bool QCPLayerable::setLayer(const QString &layerName) { |
962 |
|
|
if (!mParentPlot) { |
963 |
|
|
qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set"; |
964 |
|
|
return false; |
965 |
|
|
} |
966 |
|
|
if (QCPLayer *layer = mParentPlot->layer(layerName)) { |
967 |
|
|
return setLayer(layer); |
968 |
|
|
} else { |
969 |
|
|
qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName; |
970 |
|
|
return false; |
971 |
|
|
} |
972 |
|
|
} |
973 |
|
|
|
974 |
|
|
/*! |
975 |
|
|
Sets whether this object will be drawn antialiased or not. |
976 |
|
|
|
977 |
|
|
Note that antialiasing settings may be overridden by |
978 |
|
|
QCustomPlot::setAntialiasedElements and |
979 |
|
|
QCustomPlot::setNotAntialiasedElements. |
980 |
|
|
*/ |
981 |
|
|
void QCPLayerable::setAntialiased(bool enabled) { mAntialiased = enabled; } |
982 |
|
|
|
983 |
|
|
/*! |
984 |
|
|
Returns whether this layerable is visible, taking the visibility of the |
985 |
|
|
layerable parent and the visibility of the layer this layerable is on into |
986 |
|
|
account. This is the method that is consulted to decide whether a layerable |
987 |
|
|
shall be drawn or not. |
988 |
|
|
|
989 |
|
|
If this layerable has a direct layerable parent (usually set via hierarchies |
990 |
|
|
implemented in subclasses, like in the case of QCPLayoutElement), this |
991 |
|
|
function returns true only if this layerable has its visibility set to true |
992 |
|
|
and the parent layerable's \ref realVisibility returns true. |
993 |
|
|
|
994 |
|
|
If this layerable doesn't have a direct layerable parent, returns the state of |
995 |
|
|
this layerable's visibility. |
996 |
|
|
*/ |
997 |
|
|
bool QCPLayerable::realVisibility() const { |
998 |
|
|
return mVisible && (!mLayer || mLayer->visible()) && |
999 |
|
|
(!mParentLayerable || mParentLayerable.data()->realVisibility()); |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
/*! |
1003 |
|
|
This function is used to decide whether a click hits a layerable object or |
1004 |
|
|
not. |
1005 |
|
|
|
1006 |
|
|
\a pos is a point in pixel coordinates on the QCustomPlot surface. This |
1007 |
|
|
function returns the shortest pixel distance of this point to the object. If |
1008 |
|
|
the object is either invisible or the distance couldn't be determined, -1.0 is |
1009 |
|
|
returned. Further, if \a onlySelectable is true and the object is not |
1010 |
|
|
selectable, -1.0 is returned, too. |
1011 |
|
|
|
1012 |
|
|
If the object is represented not by single lines but by an area like a \ref |
1013 |
|
|
QCPItemText or the bars of a \ref QCPBars plottable, a click inside the area |
1014 |
|
|
should also be considered a hit. In these cases this function thus returns a |
1015 |
|
|
constant value greater zero but still below the parent plot's selection |
1016 |
|
|
tolerance. (typically the selectionTolerance multiplied by 0.99). |
1017 |
|
|
|
1018 |
|
|
Providing a constant value for area objects allows selecting line objects even |
1019 |
|
|
when they are obscured by such area objects, by clicking close to the lines |
1020 |
|
|
(i.e. closer than 0.99*selectionTolerance). |
1021 |
|
|
|
1022 |
|
|
The actual setting of the selection state is not done by this function. This |
1023 |
|
|
is handled by the parent QCustomPlot when the mouseReleaseEvent occurs, and |
1024 |
|
|
the finally selected object is notified via the selectEvent/deselectEvent |
1025 |
|
|
methods. |
1026 |
|
|
|
1027 |
|
|
\a details is an optional output parameter. Every layerable subclass may place |
1028 |
|
|
any information in \a details. This information will be passed to \ref |
1029 |
|
|
selectEvent when the parent QCustomPlot decides on the basis of this |
1030 |
|
|
selectTest call, that the object was successfully selected. The subsequent |
1031 |
|
|
call to \ref selectEvent will carry the \a details. This is useful for |
1032 |
|
|
multi-part objects (like QCPAxis). This way, a possibly complex calculation to |
1033 |
|
|
decide which part was clicked is only done once in \ref selectTest. The result |
1034 |
|
|
(i.e. the actually clicked part) can then be placed in \a details. So in the |
1035 |
|
|
subsequent \ref selectEvent, the decision which part was selected doesn't have |
1036 |
|
|
to be done a second time for a single selection operation. |
1037 |
|
|
|
1038 |
|
|
You may pass 0 as \a details to indicate that you are not interested in those |
1039 |
|
|
selection details. |
1040 |
|
|
|
1041 |
|
|
\see selectEvent, deselectEvent, QCustomPlot::setInteractions |
1042 |
|
|
*/ |
1043 |
|
|
double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, |
1044 |
|
|
QVariant *details) const { |
1045 |
|
|
Q_UNUSED(pos) |
1046 |
|
|
Q_UNUSED(onlySelectable) |
1047 |
|
|
Q_UNUSED(details) |
1048 |
|
|
return -1.0; |
1049 |
|
|
} |
1050 |
|
|
|
1051 |
|
|
/*! \internal |
1052 |
|
|
|
1053 |
|
|
Sets the parent plot of this layerable. Use this function once to set the |
1054 |
|
|
parent plot if you have passed 0 in the constructor. It can not be used to |
1055 |
|
|
move a layerable from one QCustomPlot to another one. |
1056 |
|
|
|
1057 |
|
|
Note that, unlike when passing a non-null parent plot in the constructor, this |
1058 |
|
|
function does not make \a parentPlot the QObject-parent of this layerable. If |
1059 |
|
|
you want this, call QObject::setParent(\a parentPlot) in addition to this |
1060 |
|
|
function. |
1061 |
|
|
|
1062 |
|
|
Further, you will probably want to set a layer (\ref setLayer) after calling |
1063 |
|
|
this function, to make the layerable appear on the QCustomPlot. |
1064 |
|
|
|
1065 |
|
|
The parent plot change will be propagated to subclasses via a call to \ref |
1066 |
|
|
parentPlotInitialized so they can react accordingly (e.g. also initialize the |
1067 |
|
|
parent plot of child layerables, like QCPLayout does). |
1068 |
|
|
*/ |
1069 |
|
|
void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot) { |
1070 |
|
|
if (mParentPlot) { |
1071 |
|
|
qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized"; |
1072 |
|
|
return; |
1073 |
|
|
} |
1074 |
|
|
|
1075 |
|
|
if (!parentPlot) qDebug() << Q_FUNC_INFO << "called with parentPlot zero"; |
1076 |
|
|
|
1077 |
|
|
mParentPlot = parentPlot; |
1078 |
|
|
parentPlotInitialized(mParentPlot); |
1079 |
|
|
} |
1080 |
|
|
|
1081 |
|
|
/*! \internal |
1082 |
|
|
|
1083 |
|
|
Sets the parent layerable of this layerable to \a parentLayerable. Note that |
1084 |
|
|
\a parentLayerable does not become the QObject-parent (for memory management) |
1085 |
|
|
of this layerable. |
1086 |
|
|
|
1087 |
|
|
The parent layerable has influence on the return value of the \ref |
1088 |
|
|
realVisibility method. Only layerables with a fully visible parent tree will |
1089 |
|
|
return true for \ref realVisibility, and thus be drawn. |
1090 |
|
|
|
1091 |
|
|
\see realVisibility |
1092 |
|
|
*/ |
1093 |
|
|
void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable) { |
1094 |
|
|
mParentLayerable = parentLayerable; |
1095 |
|
|
} |
1096 |
|
|
|
1097 |
|
|
/*! \internal |
1098 |
|
|
|
1099 |
|
|
Moves this layerable object to \a layer. If \a prepend is true, this object |
1100 |
|
|
will be prepended to the new layer's list, i.e. it will be drawn below the |
1101 |
|
|
objects already on the layer. If it is false, the object will be appended. |
1102 |
|
|
|
1103 |
|
|
Returns true on success, i.e. if \a layer is a valid layer. |
1104 |
|
|
*/ |
1105 |
|
|
bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) { |
1106 |
|
|
if (layer && !mParentPlot) { |
1107 |
|
|
qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set"; |
1108 |
|
|
return false; |
1109 |
|
|
} |
1110 |
|
|
if (layer && layer->parentPlot() != mParentPlot) { |
1111 |
|
|
qDebug() << Q_FUNC_INFO << "layer" << layer->name() |
1112 |
|
|
<< "is not in same QCustomPlot as this layerable"; |
1113 |
|
|
return false; |
1114 |
|
|
} |
1115 |
|
|
|
1116 |
|
|
QCPLayer *oldLayer = mLayer; |
1117 |
|
|
if (mLayer) mLayer->removeChild(this); |
1118 |
|
|
mLayer = layer; |
1119 |
|
|
if (mLayer) mLayer->addChild(this, prepend); |
1120 |
|
|
if (mLayer != oldLayer) emit layerChanged(mLayer); |
1121 |
|
|
return true; |
1122 |
|
|
} |
1123 |
|
|
|
1124 |
|
|
/*! \internal |
1125 |
|
|
|
1126 |
|
|
Sets the QCPainter::setAntialiasing state on the provided \a painter, |
1127 |
|
|
depending on the \a localAntialiased value as well as the overrides \ref |
1128 |
|
|
QCustomPlot::setAntialiasedElements and \ref |
1129 |
|
|
QCustomPlot::setNotAntialiasedElements. Which override enum this function |
1130 |
|
|
takes into account is controlled via \a overrideElement. |
1131 |
|
|
*/ |
1132 |
|
|
void QCPLayerable::applyAntialiasingHint( |
1133 |
|
|
QCPPainter *painter, bool localAntialiased, |
1134 |
|
|
QCP::AntialiasedElement overrideElement) const { |
1135 |
|
|
if (mParentPlot && |
1136 |
|
|
mParentPlot->notAntialiasedElements().testFlag(overrideElement)) |
1137 |
|
|
painter->setAntialiasing(false); |
1138 |
|
|
else if (mParentPlot && |
1139 |
|
|
mParentPlot->antialiasedElements().testFlag(overrideElement)) |
1140 |
|
|
painter->setAntialiasing(true); |
1141 |
|
|
else |
1142 |
|
|
painter->setAntialiasing(localAntialiased); |
1143 |
|
|
} |
1144 |
|
|
|
1145 |
|
|
/*! \internal |
1146 |
|
|
|
1147 |
|
|
This function is called by \ref initializeParentPlot, to allow subclasses to |
1148 |
|
|
react on the setting of a parent plot. This is the case when 0 was passed as |
1149 |
|
|
parent plot in the constructor, and the parent plot is set at a later time. |
1150 |
|
|
|
1151 |
|
|
For example, QCPLayoutElement/QCPLayout hierarchies may be created |
1152 |
|
|
independently of any QCustomPlot at first. When they are then added to a |
1153 |
|
|
layout inside the QCustomPlot, the top level element of the hierarchy gets its |
1154 |
|
|
parent plot initialized with \ref initializeParentPlot. To propagate the |
1155 |
|
|
parent plot to all the children of the hierarchy, the top level element then |
1156 |
|
|
uses this function to pass the parent plot on to its child elements. |
1157 |
|
|
|
1158 |
|
|
The default implementation does nothing. |
1159 |
|
|
|
1160 |
|
|
\see initializeParentPlot |
1161 |
|
|
*/ |
1162 |
|
|
void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot) { |
1163 |
|
|
Q_UNUSED(parentPlot) |
1164 |
|
|
} |
1165 |
|
|
|
1166 |
|
|
/*! \internal |
1167 |
|
|
|
1168 |
|
|
Returns the selection category this layerable shall belong to. The selection |
1169 |
|
|
category is used in conjunction with \ref QCustomPlot::setInteractions to |
1170 |
|
|
control which objects are selectable and which aren't. |
1171 |
|
|
|
1172 |
|
|
Subclasses that don't fit any of the normal \ref QCP::Interaction values can |
1173 |
|
|
use \ref QCP::iSelectOther. This is what the default implementation returns. |
1174 |
|
|
|
1175 |
|
|
\see QCustomPlot::setInteractions |
1176 |
|
|
*/ |
1177 |
|
|
QCP::Interaction QCPLayerable::selectionCategory() const { |
1178 |
|
|
return QCP::iSelectOther; |
1179 |
|
|
} |
1180 |
|
|
|
1181 |
|
|
/*! \internal |
1182 |
|
|
|
1183 |
|
|
Returns the clipping rectangle of this layerable object. By default, this is |
1184 |
|
|
the viewport of the parent QCustomPlot. Specific subclasses may reimplement |
1185 |
|
|
this function to provide different clipping rects. |
1186 |
|
|
|
1187 |
|
|
The returned clipping rect is set on the painter before the draw function of |
1188 |
|
|
the respective object is called. |
1189 |
|
|
*/ |
1190 |
|
|
QRect QCPLayerable::clipRect() const { |
1191 |
|
|
if (mParentPlot) |
1192 |
|
|
return mParentPlot->viewport(); |
1193 |
|
|
else |
1194 |
|
|
return QRect(); |
1195 |
|
|
} |
1196 |
|
|
|
1197 |
|
|
/*! \internal |
1198 |
|
|
|
1199 |
|
|
This event is called when the layerable shall be selected, as a consequence of |
1200 |
|
|
a click by the user. Subclasses should react to it by setting their selection |
1201 |
|
|
state appropriately. The default implementation does nothing. |
1202 |
|
|
|
1203 |
|
|
\a event is the mouse event that caused the selection. \a additive indicates, |
1204 |
|
|
whether the user was holding the multi-select-modifier while performing the |
1205 |
|
|
selection (see \ref QCustomPlot::setMultiSelectModifier). if \a additive is |
1206 |
|
|
true, the selection state must be toggled (i.e. become selected when |
1207 |
|
|
unselected and unselected when selected). |
1208 |
|
|
|
1209 |
|
|
Every selectEvent is preceded by a call to \ref selectTest, which has returned |
1210 |
|
|
positively (i.e. returned a value greater than 0 and less than the selection |
1211 |
|
|
tolerance of the parent QCustomPlot). The \a details data you output from \ref |
1212 |
|
|
selectTest is fed back via \a details here. You may use it to transport any |
1213 |
|
|
kind of information from the selectTest to the possibly subsequent |
1214 |
|
|
selectEvent. Usually \a details is used to transfer which part was clicked, if |
1215 |
|
|
it is a layerable that has multiple individually selectable parts (like |
1216 |
|
|
QCPAxis). This way selectEvent doesn't need to do the calculation again to |
1217 |
|
|
find out which part was actually clicked. |
1218 |
|
|
|
1219 |
|
|
\a selectionStateChanged is an output parameter. If the pointer is non-null, |
1220 |
|
|
this function must set the value either to true or false, depending on whether |
1221 |
|
|
the selection state of this layerable was actually changed. For layerables |
1222 |
|
|
that only are selectable as a whole and not in parts, this is simple: if \a |
1223 |
|
|
additive is true, \a selectionStateChanged must also be set to true, because |
1224 |
|
|
the selection toggles. If \a additive is false, \a selectionStateChanged is |
1225 |
|
|
only set to true, if the layerable was previously unselected and now is |
1226 |
|
|
switched to the selected state. |
1227 |
|
|
|
1228 |
|
|
\see selectTest, deselectEvent |
1229 |
|
|
*/ |
1230 |
|
|
void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, |
1231 |
|
|
const QVariant &details, |
1232 |
|
|
bool *selectionStateChanged) { |
1233 |
|
|
Q_UNUSED(event) |
1234 |
|
|
Q_UNUSED(additive) |
1235 |
|
|
Q_UNUSED(details) |
1236 |
|
|
Q_UNUSED(selectionStateChanged) |
1237 |
|
|
} |
1238 |
|
|
|
1239 |
|
|
/*! \internal |
1240 |
|
|
|
1241 |
|
|
This event is called when the layerable shall be deselected, either as |
1242 |
|
|
consequence of a user interaction or a call to \ref QCustomPlot::deselectAll. |
1243 |
|
|
Subclasses should react to it by unsetting their selection appropriately. |
1244 |
|
|
|
1245 |
|
|
just as in \ref selectEvent, the output parameter \a selectionStateChanged (if |
1246 |
|
|
non-null), must return true or false when the selection state of this |
1247 |
|
|
layerable has changed or not changed, respectively. |
1248 |
|
|
|
1249 |
|
|
\see selectTest, selectEvent |
1250 |
|
|
*/ |
1251 |
|
|
void QCPLayerable::deselectEvent(bool *selectionStateChanged) { |
1252 |
|
|
Q_UNUSED(selectionStateChanged) |
1253 |
|
|
} |
1254 |
|
|
|
1255 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
1256 |
|
|
//////////////////// QCPRange |
1257 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
1258 |
|
|
/*! \class QCPRange |
1259 |
|
|
\brief Represents the range an axis is encompassing. |
1260 |
|
|
|
1261 |
|
|
contains a \a lower and \a upper double value and provides convenience input, |
1262 |
|
|
output and modification functions. |
1263 |
|
|
|
1264 |
|
|
\see QCPAxis::setRange |
1265 |
|
|
*/ |
1266 |
|
|
|
1267 |
|
|
/*! |
1268 |
|
|
Minimum range size (\a upper - \a lower) the range changing functions will |
1269 |
|
|
accept. Smaller intervals would cause errors due to the 11-bit exponent of |
1270 |
|
|
double precision numbers, corresponding to a minimum magnitude of roughly |
1271 |
|
|
1e-308. \see validRange, maxRange |
1272 |
|
|
*/ |
1273 |
|
|
const double QCPRange::minRange = 1e-280; |
1274 |
|
|
|
1275 |
|
|
/*! |
1276 |
|
|
Maximum values (negative and positive) the range will accept in range-changing |
1277 |
|
|
functions. Larger absolute values would cause errors due to the 11-bit |
1278 |
|
|
exponent of double precision numbers, corresponding to a maximum magnitude of |
1279 |
|
|
roughly 1e308. Since the number of planck-volumes in the entire visible |
1280 |
|
|
universe is only ~1e183, this should be enough. \see validRange, minRange |
1281 |
|
|
*/ |
1282 |
|
|
const double QCPRange::maxRange = 1e250; |
1283 |
|
|
|
1284 |
|
|
/*! |
1285 |
|
|
Constructs a range with \a lower and \a upper set to zero. |
1286 |
|
|
*/ |
1287 |
|
|
QCPRange::QCPRange() : lower(0), upper(0) {} |
1288 |
|
|
|
1289 |
|
|
/*! \overload |
1290 |
|
|
Constructs a range with the specified \a lower and \a upper values. |
1291 |
|
|
*/ |
1292 |
|
|
QCPRange::QCPRange(double lower, double upper) : lower(lower), upper(upper) { |
1293 |
|
|
normalize(); |
1294 |
|
|
} |
1295 |
|
|
|
1296 |
|
|
/*! |
1297 |
|
|
Returns the size of the range, i.e. \a upper-\a lower |
1298 |
|
|
*/ |
1299 |
|
|
double QCPRange::size() const { return upper - lower; } |
1300 |
|
|
|
1301 |
|
|
/*! |
1302 |
|
|
Returns the center of the range, i.e. (\a upper+\a lower)*0.5 |
1303 |
|
|
*/ |
1304 |
|
|
double QCPRange::center() const { return (upper + lower) * 0.5; } |
1305 |
|
|
|
1306 |
|
|
/*! |
1307 |
|
|
Makes sure \a lower is numerically smaller than \a upper. If this is not the |
1308 |
|
|
case, the values are swapped. |
1309 |
|
|
*/ |
1310 |
|
|
void QCPRange::normalize() { |
1311 |
|
|
if (lower > upper) qSwap(lower, upper); |
1312 |
|
|
} |
1313 |
|
|
|
1314 |
|
|
/*! |
1315 |
|
|
Expands this range such that \a otherRange is contained in the new range. It |
1316 |
|
|
is assumed that both this range and \a otherRange are normalized (see \ref |
1317 |
|
|
normalize). |
1318 |
|
|
|
1319 |
|
|
If \a otherRange is already inside the current range, this function does |
1320 |
|
|
nothing. |
1321 |
|
|
|
1322 |
|
|
\see expanded |
1323 |
|
|
*/ |
1324 |
|
|
void QCPRange::expand(const QCPRange &otherRange) { |
1325 |
|
|
if (lower > otherRange.lower) lower = otherRange.lower; |
1326 |
|
|
if (upper < otherRange.upper) upper = otherRange.upper; |
1327 |
|
|
} |
1328 |
|
|
|
1329 |
|
|
/*! |
1330 |
|
|
Returns an expanded range that contains this and \a otherRange. It is assumed |
1331 |
|
|
that both this range and \a otherRange are normalized (see \ref normalize). |
1332 |
|
|
|
1333 |
|
|
\see expand |
1334 |
|
|
*/ |
1335 |
|
|
QCPRange QCPRange::expanded(const QCPRange &otherRange) const { |
1336 |
|
|
QCPRange result = *this; |
1337 |
|
|
result.expand(otherRange); |
1338 |
|
|
return result; |
1339 |
|
|
} |
1340 |
|
|
|
1341 |
|
|
/*! |
1342 |
|
|
Returns a sanitized version of the range. Sanitized means for logarithmic |
1343 |
|
|
scales, that the range won't span the positive and negative sign domain, i.e. |
1344 |
|
|
contain zero. Further \a lower will always be numerically smaller (or equal) |
1345 |
|
|
to \a upper. |
1346 |
|
|
|
1347 |
|
|
If the original range does span positive and negative sign domains or contains |
1348 |
|
|
zero, the returned range will try to approximate the original range as good as |
1349 |
|
|
possible. If the positive interval of the original range is wider than the |
1350 |
|
|
negative interval, the returned range will only contain the positive interval, |
1351 |
|
|
with lower bound set to \a rangeFac or \a rangeFac *\a upper, whichever is |
1352 |
|
|
closer to zero. Same procedure is used if the negative interval is wider than |
1353 |
|
|
the positive interval, this time by changing the \a upper bound. |
1354 |
|
|
*/ |
1355 |
|
|
QCPRange QCPRange::sanitizedForLogScale() const { |
1356 |
|
|
double rangeFac = 1e-3; |
1357 |
|
|
QCPRange sanitizedRange(lower, upper); |
1358 |
|
|
sanitizedRange.normalize(); |
1359 |
|
|
// can't have range spanning negative and positive values in log plot, so |
1360 |
|
|
// change range to fix it |
1361 |
|
|
// if (qFuzzyCompare(sanitizedRange.lower+1, 1) && |
1362 |
|
|
// !qFuzzyCompare(sanitizedRange.upper+1, 1)) |
1363 |
|
|
if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) { |
1364 |
|
|
// case lower is 0 |
1365 |
|
|
if (rangeFac < sanitizedRange.upper * rangeFac) |
1366 |
|
|
sanitizedRange.lower = rangeFac; |
1367 |
|
|
else |
1368 |
|
|
sanitizedRange.lower = sanitizedRange.upper * rangeFac; |
1369 |
|
|
} // else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1)) |
1370 |
|
|
else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) { |
1371 |
|
|
// case upper is 0 |
1372 |
|
|
if (-rangeFac > sanitizedRange.lower * rangeFac) |
1373 |
|
|
sanitizedRange.upper = -rangeFac; |
1374 |
|
|
else |
1375 |
|
|
sanitizedRange.upper = sanitizedRange.lower * rangeFac; |
1376 |
|
|
} else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) { |
1377 |
|
|
// find out whether negative or positive interval is wider to decide which |
1378 |
|
|
// sign domain will be chosen |
1379 |
|
|
if (-sanitizedRange.lower > sanitizedRange.upper) { |
1380 |
|
|
// negative is wider, do same as in case upper is 0 |
1381 |
|
|
if (-rangeFac > sanitizedRange.lower * rangeFac) |
1382 |
|
|
sanitizedRange.upper = -rangeFac; |
1383 |
|
|
else |
1384 |
|
|
sanitizedRange.upper = sanitizedRange.lower * rangeFac; |
1385 |
|
|
} else { |
1386 |
|
|
// positive is wider, do same as in case lower is 0 |
1387 |
|
|
if (rangeFac < sanitizedRange.upper * rangeFac) |
1388 |
|
|
sanitizedRange.lower = rangeFac; |
1389 |
|
|
else |
1390 |
|
|
sanitizedRange.lower = sanitizedRange.upper * rangeFac; |
1391 |
|
|
} |
1392 |
|
|
} |
1393 |
|
|
// due to normalization, case lower>0 && upper<0 should never occur, because |
1394 |
|
|
// that implies upper<lower |
1395 |
|
|
return sanitizedRange; |
1396 |
|
|
} |
1397 |
|
|
|
1398 |
|
|
/*! |
1399 |
|
|
Returns a sanitized version of the range. Sanitized means for linear scales, |
1400 |
|
|
that \a lower will always be numerically smaller (or equal) to \a upper. |
1401 |
|
|
*/ |
1402 |
|
|
QCPRange QCPRange::sanitizedForLinScale() const { |
1403 |
|
|
QCPRange sanitizedRange(lower, upper); |
1404 |
|
|
sanitizedRange.normalize(); |
1405 |
|
|
return sanitizedRange; |
1406 |
|
|
} |
1407 |
|
|
|
1408 |
|
|
/*! |
1409 |
|
|
Returns true when \a value lies within or exactly on the borders of the range. |
1410 |
|
|
*/ |
1411 |
|
|
bool QCPRange::contains(double value) const { |
1412 |
|
|
return value >= lower && value <= upper; |
1413 |
|
|
} |
1414 |
|
|
|
1415 |
|
|
/*! |
1416 |
|
|
Checks, whether the specified range is within valid bounds, which are defined |
1417 |
|
|
as QCPRange::maxRange and QCPRange::minRange. |
1418 |
|
|
A valid range means: |
1419 |
|
|
\li range bounds within -maxRange and maxRange |
1420 |
|
|
\li range size above minRange |
1421 |
|
|
\li range size below maxRange |
1422 |
|
|
*/ |
1423 |
|
|
bool QCPRange::validRange(double lower, double upper) { |
1424 |
|
|
return (lower > -maxRange && upper < maxRange && |
1425 |
|
|
qAbs(lower - upper) > minRange && qAbs(lower - upper) < maxRange && |
1426 |
|
|
!(lower > 0 && qIsInf(upper / lower)) && |
1427 |
|
|
!(upper < 0 && qIsInf(lower / upper))); |
1428 |
|
|
} |
1429 |
|
|
|
1430 |
|
|
/*! |
1431 |
|
|
\overload |
1432 |
|
|
Checks, whether the specified range is within valid bounds, which are defined |
1433 |
|
|
as QCPRange::maxRange and QCPRange::minRange. |
1434 |
|
|
A valid range means: |
1435 |
|
|
\li range bounds within -maxRange and maxRange |
1436 |
|
|
\li range size above minRange |
1437 |
|
|
\li range size below maxRange |
1438 |
|
|
*/ |
1439 |
|
|
bool QCPRange::validRange(const QCPRange &range) { |
1440 |
|
|
return (range.lower > -maxRange && range.upper < maxRange && |
1441 |
|
|
qAbs(range.lower - range.upper) > minRange && |
1442 |
|
|
qAbs(range.lower - range.upper) < maxRange && |
1443 |
|
|
!(range.lower > 0 && qIsInf(range.upper / range.lower)) && |
1444 |
|
|
!(range.upper < 0 && qIsInf(range.lower / range.upper))); |
1445 |
|
|
} |
1446 |
|
|
|
1447 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
1448 |
|
|
//////////////////// QCPMarginGroup |
1449 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
1450 |
|
|
|
1451 |
|
|
/*! \class QCPMarginGroup |
1452 |
|
|
\brief A margin group allows synchronization of margin sides if working with |
1453 |
|
|
multiple layout elements. |
1454 |
|
|
|
1455 |
|
|
QCPMarginGroup allows you to tie a margin side of two or more layout elements |
1456 |
|
|
together, such that they will all have the same size, based on the largest |
1457 |
|
|
required margin in the group. |
1458 |
|
|
|
1459 |
|
|
\n |
1460 |
|
|
\image html QCPMarginGroup.png "Demonstration of QCPMarginGroup" |
1461 |
|
|
\n |
1462 |
|
|
|
1463 |
|
|
In certain situations it is desirable that margins at specific sides are |
1464 |
|
|
synchronized across layout elements. For example, if one QCPAxisRect is below |
1465 |
|
|
another one in a grid layout, it will provide a cleaner look to the user if |
1466 |
|
|
the left and right margins of the two axis rects are of the same size. The |
1467 |
|
|
left axis of the top axis rect will then be at the same horizontal position as |
1468 |
|
|
the left axis of the lower axis rect, making them appear aligned. The same |
1469 |
|
|
applies for the right axes. This is what QCPMarginGroup makes possible. |
1470 |
|
|
|
1471 |
|
|
To add/remove a specific side of a layout element to/from a margin group, use |
1472 |
|
|
the \ref QCPLayoutElement::setMarginGroup method. To completely break apart |
1473 |
|
|
the margin group, either call \ref clear, or just delete the margin group. |
1474 |
|
|
|
1475 |
|
|
\section QCPMarginGroup-example Example |
1476 |
|
|
|
1477 |
|
|
First create a margin group: |
1478 |
|
|
\snippet documentation/doc-code-snippets/mainwindow.cpp |
1479 |
|
|
qcpmargingroup-creation-1 Then set this group on the layout element sides: |
1480 |
|
|
\snippet documentation/doc-code-snippets/mainwindow.cpp |
1481 |
|
|
qcpmargingroup-creation-2 Here, we've used the first two axis rects of the |
1482 |
|
|
plot and synchronized their left margins with each other and their right |
1483 |
|
|
margins with each other. |
1484 |
|
|
*/ |
1485 |
|
|
|
1486 |
|
|
/* start documentation of inline functions */ |
1487 |
|
|
|
1488 |
|
|
/*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) |
1489 |
|
|
const |
1490 |
|
|
|
1491 |
|
|
Returns a list of all layout elements that have their margin \a side |
1492 |
|
|
associated with this margin group. |
1493 |
|
|
*/ |
1494 |
|
|
|
1495 |
|
|
/* end documentation of inline functions */ |
1496 |
|
|
|
1497 |
|
|
/*! |
1498 |
|
|
Creates a new QCPMarginGroup instance in \a parentPlot. |
1499 |
|
|
*/ |
1500 |
|
|
QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) |
1501 |
|
|
: QObject(parentPlot), mParentPlot(parentPlot) { |
1502 |
|
|
mChildren.insert(QCP::msLeft, QList<QCPLayoutElement *>()); |
1503 |
|
|
mChildren.insert(QCP::msRight, QList<QCPLayoutElement *>()); |
1504 |
|
|
mChildren.insert(QCP::msTop, QList<QCPLayoutElement *>()); |
1505 |
|
|
mChildren.insert(QCP::msBottom, QList<QCPLayoutElement *>()); |
1506 |
|
|
} |
1507 |
|
|
|
1508 |
|
|
QCPMarginGroup::~QCPMarginGroup() { clear(); } |
1509 |
|
|
|
1510 |
|
|
/*! |
1511 |
|
|
Returns whether this margin group is empty. If this function returns true, no |
1512 |
|
|
layout elements use this margin group to synchronize margin sides. |
1513 |
|
|
*/ |
1514 |
|
|
bool QCPMarginGroup::isEmpty() const { |
1515 |
|
|
QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *> > it(mChildren); |
1516 |
|
|
while (it.hasNext()) { |
1517 |
|
|
it.next(); |
1518 |
|
|
if (!it.value().isEmpty()) return false; |
1519 |
|
|
} |
1520 |
|
|
return true; |
1521 |
|
|
} |
1522 |
|
|
|
1523 |
|
|
/*! |
1524 |
|
|
Clears this margin group. The synchronization of the margin sides that use |
1525 |
|
|
this margin group is lifted and they will use their individual margin sizes |
1526 |
|
|
again. |
1527 |
|
|
*/ |
1528 |
|
|
void QCPMarginGroup::clear() { |
1529 |
|
|
// make all children remove themselves from this margin group: |
1530 |
|
|
QHashIterator<QCP::MarginSide, QList<QCPLayoutElement *> > it(mChildren); |
1531 |
|
|
while (it.hasNext()) { |
1532 |
|
|
it.next(); |
1533 |
|
|
const QList<QCPLayoutElement *> elements = it.value(); |
1534 |
|
|
for (int i = elements.size() - 1; i >= 0; --i) |
1535 |
|
|
elements.at(i)->setMarginGroup( |
1536 |
|
|
it.key(), 0); // removes itself from mChildren via removeChild |
1537 |
|
|
} |
1538 |
|
|
} |
1539 |
|
|
|
1540 |
|
|
/*! \internal |
1541 |
|
|
|
1542 |
|
|
Returns the synchronized common margin for \a side. This is the margin value |
1543 |
|
|
that will be used by the layout element on the respective side, if it is part |
1544 |
|
|
of this margin group. |
1545 |
|
|
|
1546 |
|
|
The common margin is calculated by requesting the automatic margin (\ref |
1547 |
|
|
QCPLayoutElement::calculateAutoMargin) of each element associated with \a side |
1548 |
|
|
in this margin group, and choosing the largest returned value. |
1549 |
|
|
(QCPLayoutElement::minimumMargins is taken into account, too.) |
1550 |
|
|
*/ |
1551 |
|
|
int QCPMarginGroup::commonMargin(QCP::MarginSide side) const { |
1552 |
|
|
// query all automatic margins of the layout elements in this margin group |
1553 |
|
|
// side and find maximum: |
1554 |
|
|
int result = 0; |
1555 |
|
|
const QList<QCPLayoutElement *> elements = mChildren.value(side); |
1556 |
|
|
for (int i = 0; i < elements.size(); ++i) { |
1557 |
|
|
if (!elements.at(i)->autoMargins().testFlag(side)) continue; |
1558 |
|
|
int m = qMax(elements.at(i)->calculateAutoMargin(side), |
1559 |
|
|
QCP::getMarginValue(elements.at(i)->minimumMargins(), side)); |
1560 |
|
|
if (m > result) result = m; |
1561 |
|
|
} |
1562 |
|
|
return result; |
1563 |
|
|
} |
1564 |
|
|
|
1565 |
|
|
/*! \internal |
1566 |
|
|
|
1567 |
|
|
Adds \a element to the internal list of child elements, for the margin \a |
1568 |
|
|
side. |
1569 |
|
|
|
1570 |
|
|
This function does not modify the margin group property of \a element. |
1571 |
|
|
*/ |
1572 |
|
|
void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element) { |
1573 |
|
|
if (!mChildren[side].contains(element)) |
1574 |
|
|
mChildren[side].append(element); |
1575 |
|
|
else |
1576 |
|
|
qDebug() << Q_FUNC_INFO |
1577 |
|
|
<< "element is already child of this margin group side" |
1578 |
|
|
<< reinterpret_cast<quintptr>(element); |
1579 |
|
|
} |
1580 |
|
|
|
1581 |
|
|
/*! \internal |
1582 |
|
|
|
1583 |
|
|
Removes \a element from the internal list of child elements, for the margin \a |
1584 |
|
|
side. |
1585 |
|
|
|
1586 |
|
|
This function does not modify the margin group property of \a element. |
1587 |
|
|
*/ |
1588 |
|
|
void QCPMarginGroup::removeChild(QCP::MarginSide side, |
1589 |
|
|
QCPLayoutElement *element) { |
1590 |
|
|
if (!mChildren[side].removeOne(element)) |
1591 |
|
|
qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" |
1592 |
|
|
<< reinterpret_cast<quintptr>(element); |
1593 |
|
|
} |
1594 |
|
|
|
1595 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
1596 |
|
|
//////////////////// QCPLayoutElement |
1597 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
1598 |
|
|
|
1599 |
|
|
/*! \class QCPLayoutElement |
1600 |
|
|
\brief The abstract base class for all objects that form \ref thelayoutsystem |
1601 |
|
|
"the layout system". |
1602 |
|
|
|
1603 |
|
|
This is an abstract base class. As such, it can't be instantiated directly, |
1604 |
|
|
rather use one of its subclasses. |
1605 |
|
|
|
1606 |
|
|
A Layout element is a rectangular object which can be placed in layouts. It |
1607 |
|
|
has an outer rect (QCPLayoutElement::outerRect) and an inner rect (\ref |
1608 |
|
|
QCPLayoutElement::rect). The difference between outer and inner rect is called |
1609 |
|
|
its margin. The margin can either be set to automatic or manual (\ref |
1610 |
|
|
setAutoMargins) on a per-side basis. If a side is set to manual, that margin |
1611 |
|
|
can be set explicitly with \ref setMargins and will stay fixed at that value. |
1612 |
|
|
If it's set to automatic, the layout element subclass will control the value |
1613 |
|
|
itself (via \ref calculateAutoMargin). |
1614 |
|
|
|
1615 |
|
|
Layout elements can be placed in layouts (base class QCPLayout) like |
1616 |
|
|
QCPLayoutGrid. The top level layout is reachable via \ref |
1617 |
|
|
QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref QCPLayout |
1618 |
|
|
itself derives from \ref QCPLayoutElement, layouts can be nested. |
1619 |
|
|
|
1620 |
|
|
Thus in QCustomPlot one can divide layout elements into two categories: The |
1621 |
|
|
ones that are invisible by themselves, because they don't draw anything. Their |
1622 |
|
|
only purpose is to manage the position and size of other layout elements. This |
1623 |
|
|
category of layout elements usually use QCPLayout as base class. Then there is |
1624 |
|
|
the category of layout elements which actually draw something. For example, |
1625 |
|
|
QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does not |
1626 |
|
|
necessarily mean that the latter category can't have child layout elements. |
1627 |
|
|
QCPLegend for instance, actually derives from QCPLayoutGrid and the individual |
1628 |
|
|
legend items are child layout elements in the grid layout. |
1629 |
|
|
*/ |
1630 |
|
|
|
1631 |
|
|
/* start documentation of inline functions */ |
1632 |
|
|
|
1633 |
|
|
/*! \fn QCPLayout *QCPLayoutElement::layout() const |
1634 |
|
|
|
1635 |
|
|
Returns the parent layout of this layout element. |
1636 |
|
|
*/ |
1637 |
|
|
|
1638 |
|
|
/*! \fn QRect QCPLayoutElement::rect() const |
1639 |
|
|
|
1640 |
|
|
Returns the inner rect of this layout element. The inner rect is the outer |
1641 |
|
|
rect (\ref setOuterRect) shrinked by the margins (\ref setMargins, \ref |
1642 |
|
|
setAutoMargins). |
1643 |
|
|
|
1644 |
|
|
In some cases, the area between outer and inner rect is left blank. In other |
1645 |
|
|
cases the margin area is used to display peripheral graphics while the main |
1646 |
|
|
content is in the inner rect. This is where automatic margin calculation |
1647 |
|
|
becomes interesting because it allows the layout element to adapt the margins |
1648 |
|
|
to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect |
1649 |
|
|
draws the axis labels and tick labels in the margin area, thus needs to adjust |
1650 |
|
|
the margins (if \ref setAutoMargins is enabled) according to the space |
1651 |
|
|
required by the labels of the axes. |
1652 |
|
|
*/ |
1653 |
|
|
|
1654 |
|
|
/*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event) |
1655 |
|
|
|
1656 |
|
|
This event is called, if the mouse was pressed while being inside the outer |
1657 |
|
|
rect of this layout element. |
1658 |
|
|
*/ |
1659 |
|
|
|
1660 |
|
|
/*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event) |
1661 |
|
|
|
1662 |
|
|
This event is called, if the mouse is moved inside the outer rect of this |
1663 |
|
|
layout element. |
1664 |
|
|
*/ |
1665 |
|
|
|
1666 |
|
|
/*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event) |
1667 |
|
|
|
1668 |
|
|
This event is called, if the mouse was previously pressed inside the outer |
1669 |
|
|
rect of this layout element and is now released. |
1670 |
|
|
*/ |
1671 |
|
|
|
1672 |
|
|
/*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event) |
1673 |
|
|
|
1674 |
|
|
This event is called, if the mouse is double-clicked inside the outer rect of |
1675 |
|
|
this layout element. |
1676 |
|
|
*/ |
1677 |
|
|
|
1678 |
|
|
/*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event) |
1679 |
|
|
|
1680 |
|
|
This event is called, if the mouse wheel is scrolled while the cursor is |
1681 |
|
|
inside the rect of this layout element. |
1682 |
|
|
*/ |
1683 |
|
|
|
1684 |
|
|
/* end documentation of inline functions */ |
1685 |
|
|
|
1686 |
|
|
/*! |
1687 |
|
|
Creates an instance of QCPLayoutElement and sets default values. |
1688 |
|
|
*/ |
1689 |
|
|
QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) |
1690 |
|
|
: QCPLayerable( |
1691 |
|
|
parentPlot), // parenthood is changed as soon as layout element gets |
1692 |
|
|
// inserted into a layout (except for top level layout) |
1693 |
|
|
mParentLayout(0), |
1694 |
|
|
mMinimumSize(), |
1695 |
|
|
mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), |
1696 |
|
|
mRect(0, 0, 0, 0), |
1697 |
|
|
mOuterRect(0, 0, 0, 0), |
1698 |
|
|
mMargins(0, 0, 0, 0), |
1699 |
|
|
mMinimumMargins(0, 0, 0, 0), |
1700 |
|
|
mAutoMargins(QCP::msAll) {} |
1701 |
|
|
|
1702 |
|
|
QCPLayoutElement::~QCPLayoutElement() { |
1703 |
|
|
setMarginGroup(QCP::msAll, |
1704 |
|
|
0); // unregister at margin groups, if there are any |
1705 |
|
|
// unregister at layout: |
1706 |
|
|
if (qobject_cast<QCPLayout *>( |
1707 |
|
|
mParentLayout)) // the qobject_cast is just a safeguard in case the |
1708 |
|
|
// layout forgets to call clear() in its dtor and |
1709 |
|
|
// this dtor is called by QObject dtor |
1710 |
|
|
mParentLayout->take(this); |
1711 |
|
|
} |
1712 |
|
|
|
1713 |
|
|
/*! |
1714 |
|
|
Sets the outer rect of this layout element. If the layout element is inside a |
1715 |
|
|
layout, the layout sets the position and size of this layout element using |
1716 |
|
|
this function. |
1717 |
|
|
|
1718 |
|
|
Calling this function externally has no effect, since the layout will |
1719 |
|
|
overwrite any changes to the outer rect upon the next replot. |
1720 |
|
|
|
1721 |
|
|
The layout element will adapt its inner \ref rect by applying the margins |
1722 |
|
|
inward to the outer rect. |
1723 |
|
|
|
1724 |
|
|
\see rect |
1725 |
|
|
*/ |
1726 |
|
|
void QCPLayoutElement::setOuterRect(const QRect &rect) { |
1727 |
|
|
if (mOuterRect != rect) { |
1728 |
|
|
mOuterRect = rect; |
1729 |
|
|
mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), |
1730 |
|
|
-mMargins.right(), -mMargins.bottom()); |
1731 |
|
|
} |
1732 |
|
|
} |
1733 |
|
|
|
1734 |
|
|
/*! |
1735 |
|
|
Sets the margins of this layout element. If \ref setAutoMargins is disabled |
1736 |
|
|
for some or all sides, this function is used to manually set the margin on |
1737 |
|
|
those sides. Sides that are still set to be handled automatically are ignored |
1738 |
|
|
and may have any value in \a margins. |
1739 |
|
|
|
1740 |
|
|
The margin is the distance between the outer rect (controlled by the parent |
1741 |
|
|
layout via \ref setOuterRect) and the inner \ref rect (which usually contains |
1742 |
|
|
the main content of this layout element). |
1743 |
|
|
|
1744 |
|
|
\see setAutoMargins |
1745 |
|
|
*/ |
1746 |
|
|
void QCPLayoutElement::setMargins(const QMargins &margins) { |
1747 |
|
|
if (mMargins != margins) { |
1748 |
|
|
mMargins = margins; |
1749 |
|
|
mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), |
1750 |
|
|
-mMargins.right(), -mMargins.bottom()); |
1751 |
|
|
} |
1752 |
|
|
} |
1753 |
|
|
|
1754 |
|
|
/*! |
1755 |
|
|
If \ref setAutoMargins is enabled on some or all margins, this function is |
1756 |
|
|
used to provide minimum values for those margins. |
1757 |
|
|
|
1758 |
|
|
The minimum values are not enforced on margin sides that were set to be under |
1759 |
|
|
manual control via \ref setAutoMargins. |
1760 |
|
|
|
1761 |
|
|
\see setAutoMargins |
1762 |
|
|
*/ |
1763 |
|
|
void QCPLayoutElement::setMinimumMargins(const QMargins &margins) { |
1764 |
|
|
if (mMinimumMargins != margins) { |
1765 |
|
|
mMinimumMargins = margins; |
1766 |
|
|
} |
1767 |
|
|
} |
1768 |
|
|
|
1769 |
|
|
/*! |
1770 |
|
|
Sets on which sides the margin shall be calculated automatically. If a side is |
1771 |
|
|
calculated automatically, a minimum margin value may be provided with \ref |
1772 |
|
|
setMinimumMargins. If a side is set to be controlled manually, the value may |
1773 |
|
|
be specified with \ref setMargins. |
1774 |
|
|
|
1775 |
|
|
Margin sides that are under automatic control may participate in a \ref |
1776 |
|
|
QCPMarginGroup (see \ref setMarginGroup), to synchronize (align) it with other |
1777 |
|
|
layout elements in the plot. |
1778 |
|
|
|
1779 |
|
|
\see setMinimumMargins, setMargins |
1780 |
|
|
*/ |
1781 |
|
|
void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) { |
1782 |
|
|
mAutoMargins = sides; |
1783 |
|
|
} |
1784 |
|
|
|
1785 |
|
|
/*! |
1786 |
|
|
Sets the minimum size for the inner \ref rect of this layout element. A parent |
1787 |
|
|
layout tries to respect the \a size here by changing row/column sizes in the |
1788 |
|
|
layout accordingly. |
1789 |
|
|
|
1790 |
|
|
If the parent layout size is not sufficient to satisfy all minimum size |
1791 |
|
|
constraints of its child layout elements, the layout may set a size that is |
1792 |
|
|
actually smaller than \a size. QCustomPlot propagates the layout's size |
1793 |
|
|
constraints to the outside by setting its own minimum QWidget size |
1794 |
|
|
accordingly, so violations of \a size should be exceptions. |
1795 |
|
|
*/ |
1796 |
|
|
void QCPLayoutElement::setMinimumSize(const QSize &size) { |
1797 |
|
|
if (mMinimumSize != size) { |
1798 |
|
|
mMinimumSize = size; |
1799 |
|
|
if (mParentLayout) mParentLayout->sizeConstraintsChanged(); |
1800 |
|
|
} |
1801 |
|
|
} |
1802 |
|
|
|
1803 |
|
|
/*! \overload |
1804 |
|
|
|
1805 |
|
|
Sets the minimum size for the inner \ref rect of this layout element. |
1806 |
|
|
*/ |
1807 |
|
|
void QCPLayoutElement::setMinimumSize(int width, int height) { |
1808 |
|
|
setMinimumSize(QSize(width, height)); |
1809 |
|
|
} |
1810 |
|
|
|
1811 |
|
|
/*! |
1812 |
|
|
Sets the maximum size for the inner \ref rect of this layout element. A parent |
1813 |
|
|
layout tries to respect the \a size here by changing row/column sizes in the |
1814 |
|
|
layout accordingly. |
1815 |
|
|
*/ |
1816 |
|
|
void QCPLayoutElement::setMaximumSize(const QSize &size) { |
1817 |
|
|
if (mMaximumSize != size) { |
1818 |
|
|
mMaximumSize = size; |
1819 |
|
|
if (mParentLayout) mParentLayout->sizeConstraintsChanged(); |
1820 |
|
|
} |
1821 |
|
|
} |
1822 |
|
|
|
1823 |
|
|
/*! \overload |
1824 |
|
|
|
1825 |
|
|
Sets the maximum size for the inner \ref rect of this layout element. |
1826 |
|
|
*/ |
1827 |
|
|
void QCPLayoutElement::setMaximumSize(int width, int height) { |
1828 |
|
|
setMaximumSize(QSize(width, height)); |
1829 |
|
|
} |
1830 |
|
|
|
1831 |
|
|
/*! |
1832 |
|
|
Sets the margin \a group of the specified margin \a sides. |
1833 |
|
|
|
1834 |
|
|
Margin groups allow synchronizing specified margins across layout elements, |
1835 |
|
|
see the documentation of \ref QCPMarginGroup. |
1836 |
|
|
|
1837 |
|
|
To unset the margin group of \a sides, set \a group to 0. |
1838 |
|
|
|
1839 |
|
|
Note that margin groups only work for margin sides that are set to automatic |
1840 |
|
|
(\ref setAutoMargins). |
1841 |
|
|
*/ |
1842 |
|
|
void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, |
1843 |
|
|
QCPMarginGroup *group) { |
1844 |
|
|
QVector<QCP::MarginSide> sideVector; |
1845 |
|
|
if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft); |
1846 |
|
|
if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight); |
1847 |
|
|
if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop); |
1848 |
|
|
if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom); |
1849 |
|
|
|
1850 |
|
|
for (int i = 0; i < sideVector.size(); ++i) { |
1851 |
|
|
QCP::MarginSide side = sideVector.at(i); |
1852 |
|
|
if (marginGroup(side) != group) { |
1853 |
|
|
QCPMarginGroup *oldGroup = marginGroup(side); |
1854 |
|
|
if (oldGroup) // unregister at old group |
1855 |
|
|
oldGroup->removeChild(side, this); |
1856 |
|
|
|
1857 |
|
|
if (!group) // if setting to 0, remove hash entry. Else set hash entry to |
1858 |
|
|
// new group and register there |
1859 |
|
|
{ |
1860 |
|
|
mMarginGroups.remove(side); |
1861 |
|
|
} else // setting to a new group |
1862 |
|
|
{ |
1863 |
|
|
mMarginGroups[side] = group; |
1864 |
|
|
group->addChild(side, this); |
1865 |
|
|
} |
1866 |
|
|
} |
1867 |
|
|
} |
1868 |
|
|
} |
1869 |
|
|
|
1870 |
|
|
/*! |
1871 |
|
|
Updates the layout element and sub-elements. This function is automatically |
1872 |
|
|
called before every replot by the parent layout element. It is called multiple |
1873 |
|
|
times, once for every \ref UpdatePhase. The phases are run through in the |
1874 |
|
|
order of the enum values. For details about what happens at the different |
1875 |
|
|
phases, see the documentation of \ref UpdatePhase. |
1876 |
|
|
|
1877 |
|
|
Layout elements that have child elements should call the \ref update method of |
1878 |
|
|
their child elements, and pass the current \a phase unchanged. |
1879 |
|
|
|
1880 |
|
|
The default implementation executes the automatic margin mechanism in the \ref |
1881 |
|
|
upMargins phase. Subclasses should make sure to call the base class |
1882 |
|
|
implementation. |
1883 |
|
|
*/ |
1884 |
|
|
void QCPLayoutElement::update(UpdatePhase phase) { |
1885 |
|
|
if (phase == upMargins) { |
1886 |
|
|
if (mAutoMargins != QCP::msNone) { |
1887 |
|
|
// set the margins of this layout element according to automatic margin |
1888 |
|
|
// calculation, either directly or via a margin group: |
1889 |
|
|
QMargins newMargins = mMargins; |
1890 |
|
|
QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() |
1891 |
|
|
<< QCP::msLeft << QCP::msRight |
1892 |
|
|
<< QCP::msTop << QCP::msBottom; |
1893 |
|
|
foreach (QCP::MarginSide side, allMarginSides) { |
1894 |
|
|
if (mAutoMargins.testFlag( |
1895 |
|
|
side)) // this side's margin shall be calculated automatically |
1896 |
|
|
{ |
1897 |
|
|
if (mMarginGroups.contains(side)) |
1898 |
|
|
QCP::setMarginValue( |
1899 |
|
|
newMargins, side, |
1900 |
|
|
mMarginGroups[side]->commonMargin( |
1901 |
|
|
side)); // this side is part of a margin group, so get the |
1902 |
|
|
// margin value from that group |
1903 |
|
|
else |
1904 |
|
|
QCP::setMarginValue( |
1905 |
|
|
newMargins, side, |
1906 |
|
|
calculateAutoMargin( |
1907 |
|
|
side)); // this side is not part of a group, so calculate |
1908 |
|
|
// the value directly |
1909 |
|
|
// apply minimum margin restrictions: |
1910 |
|
|
if (QCP::getMarginValue(newMargins, side) < |
1911 |
|
|
QCP::getMarginValue(mMinimumMargins, side)) |
1912 |
|
|
QCP::setMarginValue(newMargins, side, |
1913 |
|
|
QCP::getMarginValue(mMinimumMargins, side)); |
1914 |
|
|
} |
1915 |
|
|
} |
1916 |
|
|
setMargins(newMargins); |
1917 |
|
|
} |
1918 |
|
|
} |
1919 |
|
|
} |
1920 |
|
|
|
1921 |
|
|
/*! |
1922 |
|
|
Returns the minimum size this layout element (the inner \ref rect) may be |
1923 |
|
|
compressed to. |
1924 |
|
|
|
1925 |
|
|
if a minimum size (\ref setMinimumSize) was not set manually, parent layouts |
1926 |
|
|
consult this function to determine the minimum allowed size of this layout |
1927 |
|
|
element. (A manual minimum size is considered set if it is non-zero.) |
1928 |
|
|
*/ |
1929 |
|
|
QSize QCPLayoutElement::minimumSizeHint() const { return mMinimumSize; } |
1930 |
|
|
|
1931 |
|
|
/*! |
1932 |
|
|
Returns the maximum size this layout element (the inner \ref rect) may be |
1933 |
|
|
expanded to. |
1934 |
|
|
|
1935 |
|
|
if a maximum size (\ref setMaximumSize) was not set manually, parent layouts |
1936 |
|
|
consult this function to determine the maximum allowed size of this layout |
1937 |
|
|
element. (A manual maximum size is considered set if it is smaller than Qt's |
1938 |
|
|
QWIDGETSIZE_MAX.) |
1939 |
|
|
*/ |
1940 |
|
|
QSize QCPLayoutElement::maximumSizeHint() const { return mMaximumSize; } |
1941 |
|
|
|
1942 |
|
|
/*! |
1943 |
|
|
Returns a list of all child elements in this layout element. If \a recursive |
1944 |
|
|
is true, all sub-child elements are included in the list, too. |
1945 |
|
|
|
1946 |
|
|
\warning There may be entries with value 0 in the returned list. (For example, |
1947 |
|
|
QCPLayoutGrid may have empty cells which yield 0 at the respective index.) |
1948 |
|
|
*/ |
1949 |
|
|
QList<QCPLayoutElement *> QCPLayoutElement::elements(bool recursive) const { |
1950 |
|
|
Q_UNUSED(recursive) |
1951 |
|
|
return QList<QCPLayoutElement *>(); |
1952 |
|
|
} |
1953 |
|
|
|
1954 |
|
|
/*! |
1955 |
|
|
Layout elements are sensitive to events inside their outer rect. If \a pos is |
1956 |
|
|
within the outer rect, this method returns a value corresponding to 0.99 times |
1957 |
|
|
the parent plot's selection tolerance. However, layout elements are not |
1958 |
|
|
selectable by default. So if \a onlySelectable is true, -1.0 is returned. |
1959 |
|
|
|
1960 |
|
|
See \ref QCPLayerable::selectTest for a general explanation of this virtual |
1961 |
|
|
method. |
1962 |
|
|
|
1963 |
|
|
QCPLayoutElement subclasses may reimplement this method to provide more |
1964 |
|
|
specific selection test behaviour. |
1965 |
|
|
*/ |
1966 |
|
|
double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, |
1967 |
|
|
QVariant *details) const { |
1968 |
|
|
Q_UNUSED(details) |
1969 |
|
|
|
1970 |
|
|
if (onlySelectable) return -1; |
1971 |
|
|
|
1972 |
|
|
if (QRectF(mOuterRect).contains(pos)) { |
1973 |
|
|
if (mParentPlot) |
1974 |
|
|
return mParentPlot->selectionTolerance() * 0.99; |
1975 |
|
|
else { |
1976 |
|
|
qDebug() << Q_FUNC_INFO << "parent plot not defined"; |
1977 |
|
|
return -1; |
1978 |
|
|
} |
1979 |
|
|
} else |
1980 |
|
|
return -1; |
1981 |
|
|
} |
1982 |
|
|
|
1983 |
|
|
/*! \internal |
1984 |
|
|
|
1985 |
|
|
propagates the parent plot initialization to all child elements, by calling |
1986 |
|
|
\ref QCPLayerable::initializeParentPlot on them. |
1987 |
|
|
*/ |
1988 |
|
|
void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot) { |
1989 |
|
|
foreach (QCPLayoutElement *el, elements(false)) { |
1990 |
|
|
if (!el->parentPlot()) el->initializeParentPlot(parentPlot); |
1991 |
|
|
} |
1992 |
|
|
} |
1993 |
|
|
|
1994 |
|
|
/*! \internal |
1995 |
|
|
|
1996 |
|
|
Returns the margin size for this \a side. It is used if automatic margins is |
1997 |
|
|
enabled for this \a side (see \ref setAutoMargins). If a minimum margin was |
1998 |
|
|
set with \ref setMinimumMargins, the returned value will not be smaller than |
1999 |
|
|
the specified minimum margin. |
2000 |
|
|
|
2001 |
|
|
The default implementation just returns the respective manual margin (\ref |
2002 |
|
|
setMargins) or the minimum margin, whichever is larger. |
2003 |
|
|
*/ |
2004 |
|
|
int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side) { |
2005 |
|
|
return qMax(QCP::getMarginValue(mMargins, side), |
2006 |
|
|
QCP::getMarginValue(mMinimumMargins, side)); |
2007 |
|
|
} |
2008 |
|
|
|
2009 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
2010 |
|
|
//////////////////// QCPLayout |
2011 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
2012 |
|
|
|
2013 |
|
|
/*! \class QCPLayout |
2014 |
|
|
\brief The abstract base class for layouts |
2015 |
|
|
|
2016 |
|
|
This is an abstract base class for layout elements whose main purpose is to |
2017 |
|
|
define the position and size of other child layout elements. In most cases, |
2018 |
|
|
layouts don't draw anything themselves (but there are exceptions to this, e.g. |
2019 |
|
|
QCPLegend). |
2020 |
|
|
|
2021 |
|
|
QCPLayout derives from QCPLayoutElement, and thus can itself be nested in |
2022 |
|
|
other layouts. |
2023 |
|
|
|
2024 |
|
|
QCPLayout introduces a common interface for accessing and manipulating the |
2025 |
|
|
child elements. Those functions are most notably \ref elementCount, \ref |
2026 |
|
|
elementAt, \ref takeAt, \ref take, \ref simplify, \ref removeAt, \ref remove |
2027 |
|
|
and \ref clear. Individual subclasses may add more functions to this interface |
2028 |
|
|
which are more specialized to the form of the layout. For example, \ref |
2029 |
|
|
QCPLayoutGrid adds functions that take row and column indices to access cells |
2030 |
|
|
of the layout grid more conveniently. |
2031 |
|
|
|
2032 |
|
|
Since this is an abstract base class, you can't instantiate it directly. |
2033 |
|
|
Rather use one of its subclasses like QCPLayoutGrid or QCPLayoutInset. |
2034 |
|
|
|
2035 |
|
|
For a general introduction to the layout system, see the dedicated |
2036 |
|
|
documentation page \ref thelayoutsystem "The Layout System". |
2037 |
|
|
*/ |
2038 |
|
|
|
2039 |
|
|
/* start documentation of pure virtual functions */ |
2040 |
|
|
|
2041 |
|
|
/*! \fn virtual int QCPLayout::elementCount() const = 0 |
2042 |
|
|
|
2043 |
|
|
Returns the number of elements/cells in the layout. |
2044 |
|
|
|
2045 |
|
|
\see elements, elementAt |
2046 |
|
|
*/ |
2047 |
|
|
|
2048 |
|
|
/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0 |
2049 |
|
|
|
2050 |
|
|
Returns the element in the cell with the given \a index. If \a index is |
2051 |
|
|
invalid, returns 0. |
2052 |
|
|
|
2053 |
|
|
Note that even if \a index is valid, the respective cell may be empty in some |
2054 |
|
|
layouts (e.g. QCPLayoutGrid), so this function may return 0 in those cases. |
2055 |
|
|
You may use this function to check whether a cell is empty or not. |
2056 |
|
|
|
2057 |
|
|
\see elements, elementCount, takeAt |
2058 |
|
|
*/ |
2059 |
|
|
|
2060 |
|
|
/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0 |
2061 |
|
|
|
2062 |
|
|
Removes the element with the given \a index from the layout and returns it. |
2063 |
|
|
|
2064 |
|
|
If the \a index is invalid or the cell with that index is empty, returns 0. |
2065 |
|
|
|
2066 |
|
|
Note that some layouts don't remove the respective cell right away but leave |
2067 |
|
|
an empty cell after successful removal of the layout element. To collapse |
2068 |
|
|
empty cells, use \ref simplify. |
2069 |
|
|
|
2070 |
|
|
\see elementAt, take |
2071 |
|
|
*/ |
2072 |
|
|
|
2073 |
|
|
/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0 |
2074 |
|
|
|
2075 |
|
|
Removes the specified \a element from the layout and returns true on success. |
2076 |
|
|
|
2077 |
|
|
If the \a element isn't in this layout, returns false. |
2078 |
|
|
|
2079 |
|
|
Note that some layouts don't remove the respective cell right away but leave |
2080 |
|
|
an empty cell after successful removal of the layout element. To collapse |
2081 |
|
|
empty cells, use \ref simplify. |
2082 |
|
|
|
2083 |
|
|
\see takeAt |
2084 |
|
|
*/ |
2085 |
|
|
|
2086 |
|
|
/* end documentation of pure virtual functions */ |
2087 |
|
|
|
2088 |
|
|
/*! |
2089 |
|
|
Creates an instance of QCPLayout and sets default values. Note that since |
2090 |
|
|
QCPLayout is an abstract base class, it can't be instantiated directly. |
2091 |
|
|
*/ |
2092 |
|
|
QCPLayout::QCPLayout() {} |
2093 |
|
|
|
2094 |
|
|
/*! |
2095 |
|
|
First calls the QCPLayoutElement::update base class implementation to update |
2096 |
|
|
the margins on this layout. |
2097 |
|
|
|
2098 |
|
|
Then calls \ref updateLayout which subclasses reimplement to reposition and |
2099 |
|
|
resize their cells. |
2100 |
|
|
|
2101 |
|
|
Finally, \ref update is called on all child elements. |
2102 |
|
|
*/ |
2103 |
|
|
void QCPLayout::update(UpdatePhase phase) { |
2104 |
|
|
QCPLayoutElement::update(phase); |
2105 |
|
|
|
2106 |
|
|
// set child element rects according to layout: |
2107 |
|
|
if (phase == upLayout) updateLayout(); |
2108 |
|
|
|
2109 |
|
|
// propagate update call to child elements: |
2110 |
|
|
const int elCount = elementCount(); |
2111 |
|
|
for (int i = 0; i < elCount; ++i) { |
2112 |
|
|
if (QCPLayoutElement *el = elementAt(i)) el->update(phase); |
2113 |
|
|
} |
2114 |
|
|
} |
2115 |
|
|
|
2116 |
|
|
/* inherits documentation from base class */ |
2117 |
|
|
QList<QCPLayoutElement *> QCPLayout::elements(bool recursive) const { |
2118 |
|
|
const int c = elementCount(); |
2119 |
|
|
QList<QCPLayoutElement *> result; |
2120 |
|
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) |
2121 |
|
|
result.reserve(c); |
2122 |
|
|
#endif |
2123 |
|
|
for (int i = 0; i < c; ++i) result.append(elementAt(i)); |
2124 |
|
|
if (recursive) { |
2125 |
|
|
for (int i = 0; i < c; ++i) { |
2126 |
|
|
if (result.at(i)) result << result.at(i)->elements(recursive); |
2127 |
|
|
} |
2128 |
|
|
} |
2129 |
|
|
return result; |
2130 |
|
|
} |
2131 |
|
|
|
2132 |
|
|
/*! |
2133 |
|
|
Simplifies the layout by collapsing empty cells. The exact behavior depends on |
2134 |
|
|
subclasses, the default implementation does nothing. |
2135 |
|
|
|
2136 |
|
|
Not all layouts need simplification. For example, QCPLayoutInset doesn't use |
2137 |
|
|
explicit simplification while QCPLayoutGrid does. |
2138 |
|
|
*/ |
2139 |
|
|
void QCPLayout::simplify() {} |
2140 |
|
|
|
2141 |
|
|
/*! |
2142 |
|
|
Removes and deletes the element at the provided \a index. Returns true on |
2143 |
|
|
success. If \a index is invalid or points to an empty cell, returns false. |
2144 |
|
|
|
2145 |
|
|
This function internally uses \ref takeAt to remove the element from the |
2146 |
|
|
layout and then deletes the returned element. Note that some layouts don't |
2147 |
|
|
remove the respective cell right away but leave an empty cell after successful |
2148 |
|
|
removal of the layout element. To collapse empty cells, use \ref simplify. |
2149 |
|
|
|
2150 |
|
|
\see remove, takeAt |
2151 |
|
|
*/ |
2152 |
|
|
bool QCPLayout::removeAt(int index) { |
2153 |
|
|
if (QCPLayoutElement *el = takeAt(index)) { |
2154 |
|
|
delete el; |
2155 |
|
|
return true; |
2156 |
|
|
} else |
2157 |
|
|
return false; |
2158 |
|
|
} |
2159 |
|
|
|
2160 |
|
|
/*! |
2161 |
|
|
Removes and deletes the provided \a element. Returns true on success. If \a |
2162 |
|
|
element is not in the layout, returns false. |
2163 |
|
|
|
2164 |
|
|
This function internally uses \ref takeAt to remove the element from the |
2165 |
|
|
layout and then deletes the element. Note that some layouts don't remove the |
2166 |
|
|
respective cell right away but leave an empty cell after successful removal of |
2167 |
|
|
the layout element. To collapse empty cells, use \ref simplify. |
2168 |
|
|
|
2169 |
|
|
\see removeAt, take |
2170 |
|
|
*/ |
2171 |
|
|
bool QCPLayout::remove(QCPLayoutElement *element) { |
2172 |
|
|
if (take(element)) { |
2173 |
|
|
delete element; |
2174 |
|
|
return true; |
2175 |
|
|
} else |
2176 |
|
|
return false; |
2177 |
|
|
} |
2178 |
|
|
|
2179 |
|
|
/*! |
2180 |
|
|
Removes and deletes all layout elements in this layout. Finally calls \ref |
2181 |
|
|
simplify to make sure all empty cells are collapsed. |
2182 |
|
|
|
2183 |
|
|
\see remove, removeAt |
2184 |
|
|
*/ |
2185 |
|
|
void QCPLayout::clear() { |
2186 |
|
|
for (int i = elementCount() - 1; i >= 0; --i) { |
2187 |
|
|
if (elementAt(i)) removeAt(i); |
2188 |
|
|
} |
2189 |
|
|
simplify(); |
2190 |
|
|
} |
2191 |
|
|
|
2192 |
|
|
/*! |
2193 |
|
|
Subclasses call this method to report changed (minimum/maximum) size |
2194 |
|
|
constraints. |
2195 |
|
|
|
2196 |
|
|
If the parent of this layout is again a QCPLayout, forwards the call to the |
2197 |
|
|
parent's \ref sizeConstraintsChanged. If the parent is a QWidget (i.e. is the |
2198 |
|
|
\ref QCustomPlot::plotLayout of QCustomPlot), calls QWidget::updateGeometry, |
2199 |
|
|
so if the QCustomPlot widget is inside a Qt QLayout, it may update itself and |
2200 |
|
|
resize cells accordingly. |
2201 |
|
|
*/ |
2202 |
|
|
void QCPLayout::sizeConstraintsChanged() const { |
2203 |
|
|
if (QWidget *w = qobject_cast<QWidget *>(parent())) |
2204 |
|
|
w->updateGeometry(); |
2205 |
|
|
else if (QCPLayout *l = qobject_cast<QCPLayout *>(parent())) |
2206 |
|
|
l->sizeConstraintsChanged(); |
2207 |
|
|
} |
2208 |
|
|
|
2209 |
|
|
/*! \internal |
2210 |
|
|
|
2211 |
|
|
Subclasses reimplement this method to update the position and sizes of the |
2212 |
|
|
child elements/cells via calling their \ref QCPLayoutElement::setOuterRect. |
2213 |
|
|
The default implementation does nothing. |
2214 |
|
|
|
2215 |
|
|
The geometry used as a reference is the inner \ref rect of this layout. Child |
2216 |
|
|
elements should stay within that rect. |
2217 |
|
|
|
2218 |
|
|
\ref getSectionSizes may help with the reimplementation of this function. |
2219 |
|
|
|
2220 |
|
|
\see update |
2221 |
|
|
*/ |
2222 |
|
|
void QCPLayout::updateLayout() {} |
2223 |
|
|
|
2224 |
|
|
/*! \internal |
2225 |
|
|
|
2226 |
|
|
Associates \a el with this layout. This is done by setting the \ref |
2227 |
|
|
QCPLayoutElement::layout, the \ref QCPLayerable::parentLayerable and the |
2228 |
|
|
QObject parent to this layout. |
2229 |
|
|
|
2230 |
|
|
Further, if \a el didn't previously have a parent plot, calls \ref |
2231 |
|
|
QCPLayerable::initializeParentPlot on \a el to set the paret plot. |
2232 |
|
|
|
2233 |
|
|
This method is used by subclass specific methods that add elements to the |
2234 |
|
|
layout. Note that this method only changes properties in \a el. The removal |
2235 |
|
|
from the old layout and the insertion into the new layout must be done |
2236 |
|
|
additionally. |
2237 |
|
|
*/ |
2238 |
|
|
void QCPLayout::adoptElement(QCPLayoutElement *el) { |
2239 |
|
|
if (el) { |
2240 |
|
|
el->mParentLayout = this; |
2241 |
|
|
el->setParentLayerable(this); |
2242 |
|
|
el->setParent(this); |
2243 |
|
|
if (!el->parentPlot()) el->initializeParentPlot(mParentPlot); |
2244 |
|
|
} else |
2245 |
|
|
qDebug() << Q_FUNC_INFO << "Null element passed"; |
2246 |
|
|
} |
2247 |
|
|
|
2248 |
|
|
/*! \internal |
2249 |
|
|
|
2250 |
|
|
Disassociates \a el from this layout. This is done by setting the \ref |
2251 |
|
|
QCPLayoutElement::layout and the \ref QCPLayerable::parentLayerable to zero. |
2252 |
|
|
The QObject parent is set to the parent QCustomPlot. |
2253 |
|
|
|
2254 |
|
|
This method is used by subclass specific methods that remove elements from the |
2255 |
|
|
layout (e.g. \ref take or \ref takeAt). Note that this method only changes |
2256 |
|
|
properties in \a el. The removal from the old layout must be done |
2257 |
|
|
additionally. |
2258 |
|
|
*/ |
2259 |
|
|
void QCPLayout::releaseElement(QCPLayoutElement *el) { |
2260 |
|
|
if (el) { |
2261 |
|
|
el->mParentLayout = 0; |
2262 |
|
|
el->setParentLayerable(0); |
2263 |
|
|
el->setParent(mParentPlot); |
2264 |
|
|
// Note: Don't initializeParentPlot(0) here, because layout element will |
2265 |
|
|
// stay in same parent plot |
2266 |
|
|
} else |
2267 |
|
|
qDebug() << Q_FUNC_INFO << "Null element passed"; |
2268 |
|
|
} |
2269 |
|
|
|
2270 |
|
|
/*! \internal |
2271 |
|
|
|
2272 |
|
|
This is a helper function for the implementation of \ref updateLayout in |
2273 |
|
|
subclasses. |
2274 |
|
|
|
2275 |
|
|
It calculates the sizes of one-dimensional sections with provided constraints |
2276 |
|
|
on maximum section sizes, minimum section sizes, relative stretch factors and |
2277 |
|
|
the final total size of all sections. |
2278 |
|
|
|
2279 |
|
|
The QVector entries refer to the sections. Thus all QVectors must have the |
2280 |
|
|
same size. |
2281 |
|
|
|
2282 |
|
|
\a maxSizes gives the maximum allowed size of each section. If there shall be |
2283 |
|
|
no maximum size imposed, set all vector values to Qt's QWIDGETSIZE_MAX. |
2284 |
|
|
|
2285 |
|
|
\a minSizes gives the minimum allowed size of each section. If there shall be |
2286 |
|
|
no minimum size imposed, set all vector values to zero. If the \a minSizes |
2287 |
|
|
entries add up to a value greater than \a totalSize, sections will be scaled |
2288 |
|
|
smaller than the proposed minimum sizes. (In other words, not exceeding the |
2289 |
|
|
allowed total size is taken to be more important than not going below minimum |
2290 |
|
|
section sizes.) |
2291 |
|
|
|
2292 |
|
|
\a stretchFactors give the relative proportions of the sections to each other. |
2293 |
|
|
If all sections shall be scaled equally, set all values equal. If the first |
2294 |
|
|
section shall be double the size of each individual other section, set the |
2295 |
|
|
first number of \a stretchFactors to double the value of the other individual |
2296 |
|
|
values (e.g. {2, 1, 1, 1}). |
2297 |
|
|
|
2298 |
|
|
\a totalSize is the value that the final section sizes will add up to. Due to |
2299 |
|
|
rounding, the actual sum may differ slightly. If you want the section sizes to |
2300 |
|
|
sum up to exactly that value, you could distribute the remaining difference on |
2301 |
|
|
the sections. |
2302 |
|
|
|
2303 |
|
|
The return value is a QVector containing the section sizes. |
2304 |
|
|
*/ |
2305 |
|
|
QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, |
2306 |
|
|
QVector<int> minSizes, |
2307 |
|
|
QVector<double> stretchFactors, |
2308 |
|
|
int totalSize) const { |
2309 |
|
|
if (maxSizes.size() != minSizes.size() || |
2310 |
|
|
minSizes.size() != stretchFactors.size()) { |
2311 |
|
|
qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes |
2312 |
|
|
<< minSizes << stretchFactors; |
2313 |
|
|
return QVector<int>(); |
2314 |
|
|
} |
2315 |
|
|
if (stretchFactors.isEmpty()) return QVector<int>(); |
2316 |
|
|
int sectionCount = stretchFactors.size(); |
2317 |
|
|
QVector<double> sectionSizes(sectionCount); |
2318 |
|
|
// if provided total size is forced smaller than total minimum size, ignore |
2319 |
|
|
// minimum sizes (squeeze sections): |
2320 |
|
|
int minSizeSum = 0; |
2321 |
|
|
for (int i = 0; i < sectionCount; ++i) minSizeSum += minSizes.at(i); |
2322 |
|
|
if (totalSize < minSizeSum) { |
2323 |
|
|
// new stretch factors are minimum sizes and minimum sizes are set to zero: |
2324 |
|
|
for (int i = 0; i < sectionCount; ++i) { |
2325 |
|
|
stretchFactors[i] = minSizes.at(i); |
2326 |
|
|
minSizes[i] = 0; |
2327 |
|
|
} |
2328 |
|
|
} |
2329 |
|
|
|
2330 |
|
|
QList<int> minimumLockedSections; |
2331 |
|
|
QList<int> unfinishedSections; |
2332 |
|
|
for (int i = 0; i < sectionCount; ++i) unfinishedSections.append(i); |
2333 |
|
|
double freeSize = totalSize; |
2334 |
|
|
|
2335 |
|
|
int outerIterations = 0; |
2336 |
|
|
while (!unfinishedSections.isEmpty() && |
2337 |
|
|
outerIterations < |
2338 |
|
|
sectionCount * 2) // the iteration check ist just a failsafe in |
2339 |
|
|
// case something really strange happens |
2340 |
|
|
{ |
2341 |
|
|
++outerIterations; |
2342 |
|
|
int innerIterations = 0; |
2343 |
|
|
while (!unfinishedSections.isEmpty() && |
2344 |
|
|
innerIterations < |
2345 |
|
|
sectionCount * 2) // the iteration check ist just a failsafe in |
2346 |
|
|
// case something really strange happens |
2347 |
|
|
{ |
2348 |
|
|
++innerIterations; |
2349 |
|
|
// find section that hits its maximum next: |
2350 |
|
|
int nextId = -1; |
2351 |
|
|
double nextMax = 1e12; |
2352 |
|
|
for (int i = 0; i < unfinishedSections.size(); ++i) { |
2353 |
|
|
int secId = unfinishedSections.at(i); |
2354 |
|
|
double hitsMaxAt = (maxSizes.at(secId) - sectionSizes.at(secId)) / |
2355 |
|
|
stretchFactors.at(secId); |
2356 |
|
|
if (hitsMaxAt < nextMax) { |
2357 |
|
|
nextMax = hitsMaxAt; |
2358 |
|
|
nextId = secId; |
2359 |
|
|
} |
2360 |
|
|
} |
2361 |
|
|
// check if that maximum is actually within the bounds of the total size |
2362 |
|
|
// (i.e. can we stretch all remaining sections so far that the found |
2363 |
|
|
// section actually hits its maximum, without exceeding the total size |
2364 |
|
|
// when we add up all sections) |
2365 |
|
|
double stretchFactorSum = 0; |
2366 |
|
|
for (int i = 0; i < unfinishedSections.size(); ++i) |
2367 |
|
|
stretchFactorSum += stretchFactors.at(unfinishedSections.at(i)); |
2368 |
|
|
double nextMaxLimit = freeSize / stretchFactorSum; |
2369 |
|
|
if (nextMax < |
2370 |
|
|
nextMaxLimit) // next maximum is actually hit, move forward to that |
2371 |
|
|
// point and fix the size of that section |
2372 |
|
|
{ |
2373 |
|
|
for (int i = 0; i < unfinishedSections.size(); ++i) { |
2374 |
|
|
sectionSizes[unfinishedSections.at(i)] += |
2375 |
|
|
nextMax * stretchFactors.at(unfinishedSections.at( |
2376 |
|
|
i)); // increment all sections |
2377 |
|
|
freeSize -= nextMax * stretchFactors.at(unfinishedSections.at(i)); |
2378 |
|
|
} |
2379 |
|
|
unfinishedSections.removeOne( |
2380 |
|
|
nextId); // exclude the section that is now at maximum from further |
2381 |
|
|
// changes |
2382 |
|
|
} else // next maximum isn't hit, just distribute rest of free space on |
2383 |
|
|
// remaining sections |
2384 |
|
|
{ |
2385 |
|
|
for (int i = 0; i < unfinishedSections.size(); ++i) |
2386 |
|
|
sectionSizes[unfinishedSections.at(i)] += |
2387 |
|
|
nextMaxLimit * stretchFactors.at(unfinishedSections.at( |
2388 |
|
|
i)); // increment all sections |
2389 |
|
|
unfinishedSections.clear(); |
2390 |
|
|
} |
2391 |
|
|
} |
2392 |
|
|
if (innerIterations == sectionCount * 2) |
2393 |
|
|
qDebug() << Q_FUNC_INFO |
2394 |
|
|
<< "Exceeded maximum expected inner iteration count, layouting " |
2395 |
|
|
"aborted. Input was:" |
2396 |
|
|
<< maxSizes << minSizes << stretchFactors << totalSize; |
2397 |
|
|
|
2398 |
|
|
// now check whether the resulting section sizes violate minimum |
2399 |
|
|
// restrictions: |
2400 |
|
|
bool foundMinimumViolation = false; |
2401 |
|
|
for (int i = 0; i < sectionSizes.size(); ++i) { |
2402 |
|
|
if (minimumLockedSections.contains(i)) continue; |
2403 |
|
|
if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum |
2404 |
|
|
{ |
2405 |
|
|
sectionSizes[i] = minSizes.at(i); // set it to minimum |
2406 |
|
|
foundMinimumViolation = |
2407 |
|
|
true; // make sure we repeat the whole optimization process |
2408 |
|
|
minimumLockedSections.append(i); |
2409 |
|
|
} |
2410 |
|
|
} |
2411 |
|
|
if (foundMinimumViolation) { |
2412 |
|
|
freeSize = totalSize; |
2413 |
|
|
for (int i = 0; i < sectionCount; ++i) { |
2414 |
|
|
if (!minimumLockedSections.contains( |
2415 |
|
|
i)) // only put sections that haven't hit their minimum back |
2416 |
|
|
// into the pool |
2417 |
|
|
unfinishedSections.append(i); |
2418 |
|
|
else |
2419 |
|
|
freeSize -= |
2420 |
|
|
sectionSizes.at(i); // remove size of minimum locked sections |
2421 |
|
|
// from available space in next round |
2422 |
|
|
} |
2423 |
|
|
// reset all section sizes to zero that are in unfinished sections (all |
2424 |
|
|
// others have been set to their minimum): |
2425 |
|
|
for (int i = 0; i < unfinishedSections.size(); ++i) |
2426 |
|
|
sectionSizes[unfinishedSections.at(i)] = 0; |
2427 |
|
|
} |
2428 |
|
|
} |
2429 |
|
|
if (outerIterations == sectionCount * 2) |
2430 |
|
|
qDebug() << Q_FUNC_INFO |
2431 |
|
|
<< "Exceeded maximum expected outer iteration count, layouting " |
2432 |
|
|
"aborted. Input was:" |
2433 |
|
|
<< maxSizes << minSizes << stretchFactors << totalSize; |
2434 |
|
|
|
2435 |
|
|
QVector<int> result(sectionCount); |
2436 |
|
|
for (int i = 0; i < sectionCount; ++i) result[i] = qRound(sectionSizes.at(i)); |
2437 |
|
|
return result; |
2438 |
|
|
} |
2439 |
|
|
|
2440 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
2441 |
|
|
//////////////////// QCPLayoutGrid |
2442 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
2443 |
|
|
|
2444 |
|
|
/*! \class QCPLayoutGrid |
2445 |
|
|
\brief A layout that arranges child elements in a grid |
2446 |
|
|
|
2447 |
|
|
Elements are laid out in a grid with configurable stretch factors (\ref |
2448 |
|
|
setColumnStretchFactor, \ref setRowStretchFactor) and spacing (\ref |
2449 |
|
|
setColumnSpacing, \ref setRowSpacing). |
2450 |
|
|
|
2451 |
|
|
Elements can be added to cells via \ref addElement. The grid is expanded if |
2452 |
|
|
the specified row or column doesn't exist yet. Whether a cell contains a valid |
2453 |
|
|
layout element can be checked with \ref hasElement, that element can be |
2454 |
|
|
retrieved with \ref element. If rows and columns that only have empty cells |
2455 |
|
|
shall be removed, call \ref simplify. Removal of elements is either done by |
2456 |
|
|
just adding the element to a different layout or by using the QCPLayout |
2457 |
|
|
interface \ref take or \ref remove. |
2458 |
|
|
|
2459 |
|
|
Row and column insertion can be performed with \ref insertRow and \ref |
2460 |
|
|
insertColumn. |
2461 |
|
|
*/ |
2462 |
|
|
|
2463 |
|
|
/*! |
2464 |
|
|
Creates an instance of QCPLayoutGrid and sets default values. |
2465 |
|
|
*/ |
2466 |
|
|
QCPLayoutGrid::QCPLayoutGrid() : mColumnSpacing(5), mRowSpacing(5) {} |
2467 |
|
|
|
2468 |
|
|
QCPLayoutGrid::~QCPLayoutGrid() { |
2469 |
|
|
// clear all child layout elements. This is important because only the |
2470 |
|
|
// specific layouts know how to handle removing elements (clear calls virtual |
2471 |
|
|
// removeAt method to do that). |
2472 |
|
|
clear(); |
2473 |
|
|
} |
2474 |
|
|
|
2475 |
|
|
/*! |
2476 |
|
|
Returns the element in the cell in \a row and \a column. |
2477 |
|
|
|
2478 |
|
|
Returns 0 if either the row/column is invalid or if the cell is empty. In |
2479 |
|
|
those cases, a qDebug message is printed. To check whether a cell exists and |
2480 |
|
|
isn't empty, use \ref hasElement. |
2481 |
|
|
|
2482 |
|
|
\see addElement, hasElement |
2483 |
|
|
*/ |
2484 |
|
|
QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const { |
2485 |
|
|
if (row >= 0 && row < mElements.size()) { |
2486 |
|
|
if (column >= 0 && column < mElements.first().size()) { |
2487 |
|
|
if (QCPLayoutElement *result = mElements.at(row).at(column)) |
2488 |
|
|
return result; |
2489 |
|
|
else |
2490 |
|
|
qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row |
2491 |
|
|
<< "Column:" << column; |
2492 |
|
|
} else |
2493 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row |
2494 |
|
|
<< "Column:" << column; |
2495 |
|
|
} else |
2496 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row |
2497 |
|
|
<< "Column:" << column; |
2498 |
|
|
return 0; |
2499 |
|
|
} |
2500 |
|
|
|
2501 |
|
|
/*! |
2502 |
|
|
Returns the number of rows in the layout. |
2503 |
|
|
|
2504 |
|
|
\see columnCount |
2505 |
|
|
*/ |
2506 |
|
|
int QCPLayoutGrid::rowCount() const { return mElements.size(); } |
2507 |
|
|
|
2508 |
|
|
/*! |
2509 |
|
|
Returns the number of columns in the layout. |
2510 |
|
|
|
2511 |
|
|
\see rowCount |
2512 |
|
|
*/ |
2513 |
|
|
int QCPLayoutGrid::columnCount() const { |
2514 |
|
|
if (mElements.size() > 0) |
2515 |
|
|
return mElements.first().size(); |
2516 |
|
|
else |
2517 |
|
|
return 0; |
2518 |
|
|
} |
2519 |
|
|
|
2520 |
|
|
/*! |
2521 |
|
|
Adds the \a element to cell with \a row and \a column. If \a element is |
2522 |
|
|
already in a layout, it is first removed from there. If \a row or \a column |
2523 |
|
|
don't exist yet, the layout is expanded accordingly. |
2524 |
|
|
|
2525 |
|
|
Returns true if the element was added successfully, i.e. if the cell at \a row |
2526 |
|
|
and \a column didn't already have an element. |
2527 |
|
|
|
2528 |
|
|
\see element, hasElement, take, remove |
2529 |
|
|
*/ |
2530 |
|
|
bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) { |
2531 |
|
|
if (element) { |
2532 |
|
|
if (!hasElement(row, column)) { |
2533 |
|
|
if (element->layout()) // remove from old layout first |
2534 |
|
|
element->layout()->take(element); |
2535 |
|
|
expandTo(row + 1, column + 1); |
2536 |
|
|
mElements[row][column] = element; |
2537 |
|
|
adoptElement(element); |
2538 |
|
|
return true; |
2539 |
|
|
} else |
2540 |
|
|
qDebug() << Q_FUNC_INFO |
2541 |
|
|
<< "There is already an element in the specified row/column:" |
2542 |
|
|
<< row << column; |
2543 |
|
|
} else |
2544 |
|
|
qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row |
2545 |
|
|
<< column; |
2546 |
|
|
return false; |
2547 |
|
|
} |
2548 |
|
|
|
2549 |
|
|
/*! |
2550 |
|
|
Returns whether the cell at \a row and \a column exists and contains a valid |
2551 |
|
|
element, i.e. isn't empty. |
2552 |
|
|
|
2553 |
|
|
\see element |
2554 |
|
|
*/ |
2555 |
|
|
bool QCPLayoutGrid::hasElement(int row, int column) { |
2556 |
|
|
if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount()) |
2557 |
|
|
return mElements.at(row).at(column); |
2558 |
|
|
else |
2559 |
|
|
return false; |
2560 |
|
|
} |
2561 |
|
|
|
2562 |
|
|
/*! |
2563 |
|
|
Sets the stretch \a factor of \a column. |
2564 |
|
|
|
2565 |
|
|
Stretch factors control the relative sizes of rows and columns. Cells will not |
2566 |
|
|
be resized beyond their minimum and maximum widths/heights (\ref |
2567 |
|
|
QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize), |
2568 |
|
|
regardless of the stretch factor. |
2569 |
|
|
|
2570 |
|
|
The default stretch factor of newly created rows/columns is 1. |
2571 |
|
|
|
2572 |
|
|
\see setColumnStretchFactors, setRowStretchFactor |
2573 |
|
|
*/ |
2574 |
|
|
void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) { |
2575 |
|
|
if (column >= 0 && column < columnCount()) { |
2576 |
|
|
if (factor > 0) |
2577 |
|
|
mColumnStretchFactors[column] = factor; |
2578 |
|
|
else |
2579 |
|
|
qDebug() << Q_FUNC_INFO |
2580 |
|
|
<< "Invalid stretch factor, must be positive:" << factor; |
2581 |
|
|
} else |
2582 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid column:" << column; |
2583 |
|
|
} |
2584 |
|
|
|
2585 |
|
|
/*! |
2586 |
|
|
Sets the stretch \a factors of all columns. \a factors must have the size \ref |
2587 |
|
|
columnCount. |
2588 |
|
|
|
2589 |
|
|
Stretch factors control the relative sizes of rows and columns. Cells will not |
2590 |
|
|
be resized beyond their minimum and maximum widths/heights (\ref |
2591 |
|
|
QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize), |
2592 |
|
|
regardless of the stretch factor. |
2593 |
|
|
|
2594 |
|
|
The default stretch factor of newly created rows/columns is 1. |
2595 |
|
|
|
2596 |
|
|
\see setColumnStretchFactor, setRowStretchFactors |
2597 |
|
|
*/ |
2598 |
|
|
void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors) { |
2599 |
|
|
if (factors.size() == mColumnStretchFactors.size()) { |
2600 |
|
|
mColumnStretchFactors = factors; |
2601 |
|
|
for (int i = 0; i < mColumnStretchFactors.size(); ++i) { |
2602 |
|
|
if (mColumnStretchFactors.at(i) <= 0) { |
2603 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" |
2604 |
|
|
<< mColumnStretchFactors.at(i); |
2605 |
|
|
mColumnStretchFactors[i] = 1; |
2606 |
|
|
} |
2607 |
|
|
} |
2608 |
|
|
} else |
2609 |
|
|
qDebug() << Q_FUNC_INFO |
2610 |
|
|
<< "Column count not equal to passed stretch factor count:" |
2611 |
|
|
<< factors; |
2612 |
|
|
} |
2613 |
|
|
|
2614 |
|
|
/*! |
2615 |
|
|
Sets the stretch \a factor of \a row. |
2616 |
|
|
|
2617 |
|
|
Stretch factors control the relative sizes of rows and columns. Cells will not |
2618 |
|
|
be resized beyond their minimum and maximum widths/heights (\ref |
2619 |
|
|
QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize), |
2620 |
|
|
regardless of the stretch factor. |
2621 |
|
|
|
2622 |
|
|
The default stretch factor of newly created rows/columns is 1. |
2623 |
|
|
|
2624 |
|
|
\see setColumnStretchFactors, setRowStretchFactor |
2625 |
|
|
*/ |
2626 |
|
|
void QCPLayoutGrid::setRowStretchFactor(int row, double factor) { |
2627 |
|
|
if (row >= 0 && row < rowCount()) { |
2628 |
|
|
if (factor > 0) |
2629 |
|
|
mRowStretchFactors[row] = factor; |
2630 |
|
|
else |
2631 |
|
|
qDebug() << Q_FUNC_INFO |
2632 |
|
|
<< "Invalid stretch factor, must be positive:" << factor; |
2633 |
|
|
} else |
2634 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid row:" << row; |
2635 |
|
|
} |
2636 |
|
|
|
2637 |
|
|
/*! |
2638 |
|
|
Sets the stretch \a factors of all rows. \a factors must have the size \ref |
2639 |
|
|
rowCount. |
2640 |
|
|
|
2641 |
|
|
Stretch factors control the relative sizes of rows and columns. Cells will not |
2642 |
|
|
be resized beyond their minimum and maximum widths/heights (\ref |
2643 |
|
|
QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize), |
2644 |
|
|
regardless of the stretch factor. |
2645 |
|
|
|
2646 |
|
|
The default stretch factor of newly created rows/columns is 1. |
2647 |
|
|
|
2648 |
|
|
\see setRowStretchFactor, setColumnStretchFactors |
2649 |
|
|
*/ |
2650 |
|
|
void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors) { |
2651 |
|
|
if (factors.size() == mRowStretchFactors.size()) { |
2652 |
|
|
mRowStretchFactors = factors; |
2653 |
|
|
for (int i = 0; i < mRowStretchFactors.size(); ++i) { |
2654 |
|
|
if (mRowStretchFactors.at(i) <= 0) { |
2655 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" |
2656 |
|
|
<< mRowStretchFactors.at(i); |
2657 |
|
|
mRowStretchFactors[i] = 1; |
2658 |
|
|
} |
2659 |
|
|
} |
2660 |
|
|
} else |
2661 |
|
|
qDebug() << Q_FUNC_INFO |
2662 |
|
|
<< "Row count not equal to passed stretch factor count:" |
2663 |
|
|
<< factors; |
2664 |
|
|
} |
2665 |
|
|
|
2666 |
|
|
/*! |
2667 |
|
|
Sets the gap that is left blank between columns to \a pixels. |
2668 |
|
|
|
2669 |
|
|
\see setRowSpacing |
2670 |
|
|
*/ |
2671 |
|
|
void QCPLayoutGrid::setColumnSpacing(int pixels) { mColumnSpacing = pixels; } |
2672 |
|
|
|
2673 |
|
|
/*! |
2674 |
|
|
Sets the gap that is left blank between rows to \a pixels. |
2675 |
|
|
|
2676 |
|
|
\see setColumnSpacing |
2677 |
|
|
*/ |
2678 |
|
|
void QCPLayoutGrid::setRowSpacing(int pixels) { mRowSpacing = pixels; } |
2679 |
|
|
|
2680 |
|
|
/*! |
2681 |
|
|
Expands the layout to have \a newRowCount rows and \a newColumnCount columns. |
2682 |
|
|
So the last valid row index will be \a newRowCount-1, the last valid column |
2683 |
|
|
index will be \a newColumnCount-1. |
2684 |
|
|
|
2685 |
|
|
If the current column/row count is already larger or equal to \a |
2686 |
|
|
newColumnCount/\a newRowCount, this function does nothing in that dimension. |
2687 |
|
|
|
2688 |
|
|
Newly created cells are empty, new rows and columns have the stretch factor 1. |
2689 |
|
|
|
2690 |
|
|
Note that upon a call to \ref addElement, the layout is expanded automatically |
2691 |
|
|
to contain the specified row and column, using this function. |
2692 |
|
|
|
2693 |
|
|
\see simplify |
2694 |
|
|
*/ |
2695 |
|
|
void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount) { |
2696 |
|
|
// add rows as necessary: |
2697 |
|
|
while (rowCount() < newRowCount) { |
2698 |
|
|
mElements.append(QList<QCPLayoutElement *>()); |
2699 |
|
|
mRowStretchFactors.append(1); |
2700 |
|
|
} |
2701 |
|
|
// go through rows and expand columns as necessary: |
2702 |
|
|
int newColCount = qMax(columnCount(), newColumnCount); |
2703 |
|
|
for (int i = 0; i < rowCount(); ++i) { |
2704 |
|
|
while (mElements.at(i).size() < newColCount) mElements[i].append(0); |
2705 |
|
|
} |
2706 |
|
|
while (mColumnStretchFactors.size() < newColCount) |
2707 |
|
|
mColumnStretchFactors.append(1); |
2708 |
|
|
} |
2709 |
|
|
|
2710 |
|
|
/*! |
2711 |
|
|
Inserts a new row with empty cells at the row index \a newIndex. Valid values |
2712 |
|
|
for \a newIndex range from 0 (inserts a row at the top) to \a rowCount |
2713 |
|
|
(appends a row at the bottom). |
2714 |
|
|
|
2715 |
|
|
\see insertColumn |
2716 |
|
|
*/ |
2717 |
|
|
void QCPLayoutGrid::insertRow(int newIndex) { |
2718 |
|
|
if (mElements.isEmpty() || |
2719 |
|
|
mElements.first() |
2720 |
|
|
.isEmpty()) // if grid is completely empty, add first cell |
2721 |
|
|
{ |
2722 |
|
|
expandTo(1, 1); |
2723 |
|
|
return; |
2724 |
|
|
} |
2725 |
|
|
|
2726 |
|
|
if (newIndex < 0) newIndex = 0; |
2727 |
|
|
if (newIndex > rowCount()) newIndex = rowCount(); |
2728 |
|
|
|
2729 |
|
|
mRowStretchFactors.insert(newIndex, 1); |
2730 |
|
|
QList<QCPLayoutElement *> newRow; |
2731 |
|
|
for (int col = 0; col < columnCount(); ++col) |
2732 |
|
|
newRow.append((QCPLayoutElement *)0); |
2733 |
|
|
mElements.insert(newIndex, newRow); |
2734 |
|
|
} |
2735 |
|
|
|
2736 |
|
|
/*! |
2737 |
|
|
Inserts a new column with empty cells at the column index \a newIndex. Valid |
2738 |
|
|
values for \a newIndex range from 0 (inserts a row at the left) to \a rowCount |
2739 |
|
|
(appends a row at the right). |
2740 |
|
|
|
2741 |
|
|
\see insertRow |
2742 |
|
|
*/ |
2743 |
|
|
void QCPLayoutGrid::insertColumn(int newIndex) { |
2744 |
|
|
if (mElements.isEmpty() || |
2745 |
|
|
mElements.first() |
2746 |
|
|
.isEmpty()) // if grid is completely empty, add first cell |
2747 |
|
|
{ |
2748 |
|
|
expandTo(1, 1); |
2749 |
|
|
return; |
2750 |
|
|
} |
2751 |
|
|
|
2752 |
|
|
if (newIndex < 0) newIndex = 0; |
2753 |
|
|
if (newIndex > columnCount()) newIndex = columnCount(); |
2754 |
|
|
|
2755 |
|
|
mColumnStretchFactors.insert(newIndex, 1); |
2756 |
|
|
for (int row = 0; row < rowCount(); ++row) |
2757 |
|
|
mElements[row].insert(newIndex, (QCPLayoutElement *)0); |
2758 |
|
|
} |
2759 |
|
|
|
2760 |
|
|
/* inherits documentation from base class */ |
2761 |
|
|
void QCPLayoutGrid::updateLayout() { |
2762 |
|
|
QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights; |
2763 |
|
|
getMinimumRowColSizes(&minColWidths, &minRowHeights); |
2764 |
|
|
getMaximumRowColSizes(&maxColWidths, &maxRowHeights); |
2765 |
|
|
|
2766 |
|
|
int totalRowSpacing = (rowCount() - 1) * mRowSpacing; |
2767 |
|
|
int totalColSpacing = (columnCount() - 1) * mColumnSpacing; |
2768 |
|
|
QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, |
2769 |
|
|
mColumnStretchFactors.toVector(), |
2770 |
|
|
mRect.width() - totalColSpacing); |
2771 |
|
|
QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, |
2772 |
|
|
mRowStretchFactors.toVector(), |
2773 |
|
|
mRect.height() - totalRowSpacing); |
2774 |
|
|
|
2775 |
|
|
// go through cells and set rects accordingly: |
2776 |
|
|
int yOffset = mRect.top(); |
2777 |
|
|
for (int row = 0; row < rowCount(); ++row) { |
2778 |
|
|
if (row > 0) yOffset += rowHeights.at(row - 1) + mRowSpacing; |
2779 |
|
|
int xOffset = mRect.left(); |
2780 |
|
|
for (int col = 0; col < columnCount(); ++col) { |
2781 |
|
|
if (col > 0) xOffset += colWidths.at(col - 1) + mColumnSpacing; |
2782 |
|
|
if (mElements.at(row).at(col)) |
2783 |
|
|
mElements.at(row).at(col)->setOuterRect( |
2784 |
|
|
QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row))); |
2785 |
|
|
} |
2786 |
|
|
} |
2787 |
|
|
} |
2788 |
|
|
|
2789 |
|
|
/* inherits documentation from base class */ |
2790 |
|
|
int QCPLayoutGrid::elementCount() const { return rowCount() * columnCount(); } |
2791 |
|
|
|
2792 |
|
|
/* inherits documentation from base class */ |
2793 |
|
|
QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const { |
2794 |
|
|
if (index >= 0 && index < elementCount()) |
2795 |
|
|
return mElements.at(index / columnCount()).at(index % columnCount()); |
2796 |
|
|
else |
2797 |
|
|
return 0; |
2798 |
|
|
} |
2799 |
|
|
|
2800 |
|
|
/* inherits documentation from base class */ |
2801 |
|
|
QCPLayoutElement *QCPLayoutGrid::takeAt(int index) { |
2802 |
|
|
if (QCPLayoutElement *el = elementAt(index)) { |
2803 |
|
|
releaseElement(el); |
2804 |
|
|
mElements[index / columnCount()][index % columnCount()] = 0; |
2805 |
|
|
return el; |
2806 |
|
|
} else { |
2807 |
|
|
qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; |
2808 |
|
|
return 0; |
2809 |
|
|
} |
2810 |
|
|
} |
2811 |
|
|
|
2812 |
|
|
/* inherits documentation from base class */ |
2813 |
|
|
bool QCPLayoutGrid::take(QCPLayoutElement *element) { |
2814 |
|
|
if (element) { |
2815 |
|
|
for (int i = 0; i < elementCount(); ++i) { |
2816 |
|
|
if (elementAt(i) == element) { |
2817 |
|
|
takeAt(i); |
2818 |
|
|
return true; |
2819 |
|
|
} |
2820 |
|
|
} |
2821 |
|
|
qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take"; |
2822 |
|
|
} else |
2823 |
|
|
qDebug() << Q_FUNC_INFO << "Can't take null element"; |
2824 |
|
|
return false; |
2825 |
|
|
} |
2826 |
|
|
|
2827 |
|
|
/* inherits documentation from base class */ |
2828 |
|
|
QList<QCPLayoutElement *> QCPLayoutGrid::elements(bool recursive) const { |
2829 |
|
|
QList<QCPLayoutElement *> result; |
2830 |
|
|
int colC = columnCount(); |
2831 |
|
|
int rowC = rowCount(); |
2832 |
|
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) |
2833 |
|
|
result.reserve(colC * rowC); |
2834 |
|
|
#endif |
2835 |
|
|
for (int row = 0; row < rowC; ++row) { |
2836 |
|
|
for (int col = 0; col < colC; ++col) { |
2837 |
|
|
result.append(mElements.at(row).at(col)); |
2838 |
|
|
} |
2839 |
|
|
} |
2840 |
|
|
if (recursive) { |
2841 |
|
|
int c = result.size(); |
2842 |
|
|
for (int i = 0; i < c; ++i) { |
2843 |
|
|
if (result.at(i)) result << result.at(i)->elements(recursive); |
2844 |
|
|
} |
2845 |
|
|
} |
2846 |
|
|
return result; |
2847 |
|
|
} |
2848 |
|
|
|
2849 |
|
|
/*! |
2850 |
|
|
Simplifies the layout by collapsing rows and columns which only contain empty |
2851 |
|
|
cells. |
2852 |
|
|
*/ |
2853 |
|
|
void QCPLayoutGrid::simplify() { |
2854 |
|
|
// remove rows with only empty cells: |
2855 |
|
|
for (int row = rowCount() - 1; row >= 0; --row) { |
2856 |
|
|
bool hasElements = false; |
2857 |
|
|
for (int col = 0; col < columnCount(); ++col) { |
2858 |
|
|
if (mElements.at(row).at(col)) { |
2859 |
|
|
hasElements = true; |
2860 |
|
|
break; |
2861 |
|
|
} |
2862 |
|
|
} |
2863 |
|
|
if (!hasElements) { |
2864 |
|
|
mRowStretchFactors.removeAt(row); |
2865 |
|
|
mElements.removeAt(row); |
2866 |
|
|
if (mElements.isEmpty()) // removed last element, also remove stretch |
2867 |
|
|
// factor (wouldn't happen below because also |
2868 |
|
|
// columnCount changed to 0 now) |
2869 |
|
|
mColumnStretchFactors.clear(); |
2870 |
|
|
} |
2871 |
|
|
} |
2872 |
|
|
|
2873 |
|
|
// remove columns with only empty cells: |
2874 |
|
|
for (int col = columnCount() - 1; col >= 0; --col) { |
2875 |
|
|
bool hasElements = false; |
2876 |
|
|
for (int row = 0; row < rowCount(); ++row) { |
2877 |
|
|
if (mElements.at(row).at(col)) { |
2878 |
|
|
hasElements = true; |
2879 |
|
|
break; |
2880 |
|
|
} |
2881 |
|
|
} |
2882 |
|
|
if (!hasElements) { |
2883 |
|
|
mColumnStretchFactors.removeAt(col); |
2884 |
|
|
for (int row = 0; row < rowCount(); ++row) mElements[row].removeAt(col); |
2885 |
|
|
} |
2886 |
|
|
} |
2887 |
|
|
} |
2888 |
|
|
|
2889 |
|
|
/* inherits documentation from base class */ |
2890 |
|
|
QSize QCPLayoutGrid::minimumSizeHint() const { |
2891 |
|
|
QVector<int> minColWidths, minRowHeights; |
2892 |
|
|
getMinimumRowColSizes(&minColWidths, &minRowHeights); |
2893 |
|
|
QSize result(0, 0); |
2894 |
|
|
for (int i = 0; i < minColWidths.size(); ++i) |
2895 |
|
|
result.rwidth() += minColWidths.at(i); |
2896 |
|
|
for (int i = 0; i < minRowHeights.size(); ++i) |
2897 |
|
|
result.rheight() += minRowHeights.at(i); |
2898 |
|
|
result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing + |
2899 |
|
|
mMargins.left() + mMargins.right(); |
2900 |
|
|
result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() + |
2901 |
|
|
mMargins.bottom(); |
2902 |
|
|
return result; |
2903 |
|
|
} |
2904 |
|
|
|
2905 |
|
|
/* inherits documentation from base class */ |
2906 |
|
|
QSize QCPLayoutGrid::maximumSizeHint() const { |
2907 |
|
|
QVector<int> maxColWidths, maxRowHeights; |
2908 |
|
|
getMaximumRowColSizes(&maxColWidths, &maxRowHeights); |
2909 |
|
|
|
2910 |
|
|
QSize result(0, 0); |
2911 |
|
|
for (int i = 0; i < maxColWidths.size(); ++i) |
2912 |
|
|
result.setWidth(qMin(result.width() + maxColWidths.at(i), QWIDGETSIZE_MAX)); |
2913 |
|
|
for (int i = 0; i < maxRowHeights.size(); ++i) |
2914 |
|
|
result.setHeight( |
2915 |
|
|
qMin(result.height() + maxRowHeights.at(i), QWIDGETSIZE_MAX)); |
2916 |
|
|
result.rwidth() += qMax(0, columnCount() - 1) * mColumnSpacing + |
2917 |
|
|
mMargins.left() + mMargins.right(); |
2918 |
|
|
result.rheight() += qMax(0, rowCount() - 1) * mRowSpacing + mMargins.top() + |
2919 |
|
|
mMargins.bottom(); |
2920 |
|
|
return result; |
2921 |
|
|
} |
2922 |
|
|
|
2923 |
|
|
/*! \internal |
2924 |
|
|
|
2925 |
|
|
Places the minimum column widths and row heights into \a minColWidths and \a |
2926 |
|
|
minRowHeights respectively. |
2927 |
|
|
|
2928 |
|
|
The minimum height of a row is the largest minimum height of any element in |
2929 |
|
|
that row. The minimum width of a column is the largest minimum width of any |
2930 |
|
|
element in that column. |
2931 |
|
|
|
2932 |
|
|
This is a helper function for \ref updateLayout. |
2933 |
|
|
|
2934 |
|
|
\see getMaximumRowColSizes |
2935 |
|
|
*/ |
2936 |
|
|
void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, |
2937 |
|
|
QVector<int> *minRowHeights) const { |
2938 |
|
|
*minColWidths = QVector<int>(columnCount(), 0); |
2939 |
|
|
*minRowHeights = QVector<int>(rowCount(), 0); |
2940 |
|
|
for (int row = 0; row < rowCount(); ++row) { |
2941 |
|
|
for (int col = 0; col < columnCount(); ++col) { |
2942 |
|
|
if (mElements.at(row).at(col)) { |
2943 |
|
|
QSize minHint = mElements.at(row).at(col)->minimumSizeHint(); |
2944 |
|
|
QSize min = mElements.at(row).at(col)->minimumSize(); |
2945 |
|
|
QSize final(min.width() > 0 ? min.width() : minHint.width(), |
2946 |
|
|
min.height() > 0 ? min.height() : minHint.height()); |
2947 |
|
|
if (minColWidths->at(col) < final.width()) |
2948 |
|
|
(*minColWidths)[col] = final.width(); |
2949 |
|
|
if (minRowHeights->at(row) < final.height()) |
2950 |
|
|
(*minRowHeights)[row] = final.height(); |
2951 |
|
|
} |
2952 |
|
|
} |
2953 |
|
|
} |
2954 |
|
|
} |
2955 |
|
|
|
2956 |
|
|
/*! \internal |
2957 |
|
|
|
2958 |
|
|
Places the maximum column widths and row heights into \a maxColWidths and \a |
2959 |
|
|
maxRowHeights respectively. |
2960 |
|
|
|
2961 |
|
|
The maximum height of a row is the smallest maximum height of any element in |
2962 |
|
|
that row. The maximum width of a column is the smallest maximum width of any |
2963 |
|
|
element in that column. |
2964 |
|
|
|
2965 |
|
|
This is a helper function for \ref updateLayout. |
2966 |
|
|
|
2967 |
|
|
\see getMinimumRowColSizes |
2968 |
|
|
*/ |
2969 |
|
|
void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, |
2970 |
|
|
QVector<int> *maxRowHeights) const { |
2971 |
|
|
*maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX); |
2972 |
|
|
*maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX); |
2973 |
|
|
for (int row = 0; row < rowCount(); ++row) { |
2974 |
|
|
for (int col = 0; col < columnCount(); ++col) { |
2975 |
|
|
if (mElements.at(row).at(col)) { |
2976 |
|
|
QSize maxHint = mElements.at(row).at(col)->maximumSizeHint(); |
2977 |
|
|
QSize max = mElements.at(row).at(col)->maximumSize(); |
2978 |
|
|
QSize final( |
2979 |
|
|
max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), |
2980 |
|
|
max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height()); |
2981 |
|
|
if (maxColWidths->at(col) > final.width()) |
2982 |
|
|
(*maxColWidths)[col] = final.width(); |
2983 |
|
|
if (maxRowHeights->at(row) > final.height()) |
2984 |
|
|
(*maxRowHeights)[row] = final.height(); |
2985 |
|
|
} |
2986 |
|
|
} |
2987 |
|
|
} |
2988 |
|
|
} |
2989 |
|
|
|
2990 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
2991 |
|
|
//////////////////// QCPLayoutInset |
2992 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
2993 |
|
|
/*! \class QCPLayoutInset |
2994 |
|
|
\brief A layout that places child elements aligned to the border or |
2995 |
|
|
arbitrarily positioned |
2996 |
|
|
|
2997 |
|
|
Elements are placed either aligned to the border or at arbitrary position in |
2998 |
|
|
the area of the layout. Which placement applies is controlled with the \ref |
2999 |
|
|
InsetPlacement (\ref setInsetPlacement). |
3000 |
|
|
|
3001 |
|
|
Elements are added via \ref addElement(QCPLayoutElement *element, |
3002 |
|
|
Qt::Alignment alignment) or addElement(QCPLayoutElement *element, const QRectF |
3003 |
|
|
&rect). If the first method is used, the inset placement will default to \ref |
3004 |
|
|
ipBorderAligned and the element will be aligned according to the \a alignment |
3005 |
|
|
parameter. The second method defaults to \ref ipFree and allows placing |
3006 |
|
|
elements at arbitrary position and size, defined by \a rect. |
3007 |
|
|
|
3008 |
|
|
The alignment or rect can be set via \ref setInsetAlignment or \ref |
3009 |
|
|
setInsetRect, respectively. |
3010 |
|
|
|
3011 |
|
|
This is the layout that every QCPAxisRect has as \ref |
3012 |
|
|
QCPAxisRect::insetLayout. |
3013 |
|
|
*/ |
3014 |
|
|
|
3015 |
|
|
/* start documentation of inline functions */ |
3016 |
|
|
|
3017 |
|
|
/*! \fn virtual void QCPLayoutInset::simplify() |
3018 |
|
|
|
3019 |
|
|
The QCPInsetLayout does not need simplification since it can never have empty |
3020 |
|
|
cells due to its linear index structure. This method does nothing. |
3021 |
|
|
*/ |
3022 |
|
|
|
3023 |
|
|
/* end documentation of inline functions */ |
3024 |
|
|
|
3025 |
|
|
/*! |
3026 |
|
|
Creates an instance of QCPLayoutInset and sets default values. |
3027 |
|
|
*/ |
3028 |
|
|
QCPLayoutInset::QCPLayoutInset() {} |
3029 |
|
|
|
3030 |
|
|
QCPLayoutInset::~QCPLayoutInset() { |
3031 |
|
|
// clear all child layout elements. This is important because only the |
3032 |
|
|
// specific layouts know how to handle removing elements (clear calls virtual |
3033 |
|
|
// removeAt method to do that). |
3034 |
|
|
clear(); |
3035 |
|
|
} |
3036 |
|
|
|
3037 |
|
|
/*! |
3038 |
|
|
Returns the placement type of the element with the specified \a index. |
3039 |
|
|
*/ |
3040 |
|
|
QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const { |
3041 |
|
|
if (elementAt(index)) |
3042 |
|
|
return mInsetPlacement.at(index); |
3043 |
|
|
else { |
3044 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; |
3045 |
|
|
return ipFree; |
3046 |
|
|
} |
3047 |
|
|
} |
3048 |
|
|
|
3049 |
|
|
/*! |
3050 |
|
|
Returns the alignment of the element with the specified \a index. The |
3051 |
|
|
alignment only has a meaning, if the inset placement (\ref setInsetPlacement) |
3052 |
|
|
is \ref ipBorderAligned. |
3053 |
|
|
*/ |
3054 |
|
|
Qt::Alignment QCPLayoutInset::insetAlignment(int index) const { |
3055 |
|
|
if (elementAt(index)) |
3056 |
|
|
return mInsetAlignment.at(index); |
3057 |
|
|
else { |
3058 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; |
3059 |
|
|
return 0; |
3060 |
|
|
} |
3061 |
|
|
} |
3062 |
|
|
|
3063 |
|
|
/*! |
3064 |
|
|
Returns the rect of the element with the specified \a index. The rect only has |
3065 |
|
|
a meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree. |
3066 |
|
|
*/ |
3067 |
|
|
QRectF QCPLayoutInset::insetRect(int index) const { |
3068 |
|
|
if (elementAt(index)) |
3069 |
|
|
return mInsetRect.at(index); |
3070 |
|
|
else { |
3071 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; |
3072 |
|
|
return QRectF(); |
3073 |
|
|
} |
3074 |
|
|
} |
3075 |
|
|
|
3076 |
|
|
/*! |
3077 |
|
|
Sets the inset placement type of the element with the specified \a index to \a |
3078 |
|
|
placement. |
3079 |
|
|
|
3080 |
|
|
\see InsetPlacement |
3081 |
|
|
*/ |
3082 |
|
|
void QCPLayoutInset::setInsetPlacement( |
3083 |
|
|
int index, QCPLayoutInset::InsetPlacement placement) { |
3084 |
|
|
if (elementAt(index)) |
3085 |
|
|
mInsetPlacement[index] = placement; |
3086 |
|
|
else |
3087 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; |
3088 |
|
|
} |
3089 |
|
|
|
3090 |
|
|
/*! |
3091 |
|
|
If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this |
3092 |
|
|
function is used to set the alignment of the element with the specified \a |
3093 |
|
|
index to \a alignment. |
3094 |
|
|
|
3095 |
|
|
\a alignment is an or combination of the following alignment flags: |
3096 |
|
|
Qt::AlignLeft, Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, |
3097 |
|
|
Qt::AlignVCenter, Qt::AlignBottom. Any other alignment flags will be ignored. |
3098 |
|
|
*/ |
3099 |
|
|
void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment) { |
3100 |
|
|
if (elementAt(index)) |
3101 |
|
|
mInsetAlignment[index] = alignment; |
3102 |
|
|
else |
3103 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; |
3104 |
|
|
} |
3105 |
|
|
|
3106 |
|
|
/*! |
3107 |
|
|
If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function |
3108 |
|
|
is used to set the position and size of the element with the specified \a |
3109 |
|
|
index to \a rect. |
3110 |
|
|
|
3111 |
|
|
\a rect is given in fractions of the whole inset layout rect. So an inset with |
3112 |
|
|
rect (0, 0, 1, 1) will span the entire layout. An inset with rect (0.6, 0.1, |
3113 |
|
|
0.35, 0.35) will be in the top right corner of the layout, with 35% width and |
3114 |
|
|
height of the parent layout. |
3115 |
|
|
|
3116 |
|
|
Note that the minimum and maximum sizes of the embedded element (\ref |
3117 |
|
|
QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are |
3118 |
|
|
enforced. |
3119 |
|
|
*/ |
3120 |
|
|
void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) { |
3121 |
|
|
if (elementAt(index)) |
3122 |
|
|
mInsetRect[index] = rect; |
3123 |
|
|
else |
3124 |
|
|
qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; |
3125 |
|
|
} |
3126 |
|
|
|
3127 |
|
|
/* inherits documentation from base class */ |
3128 |
|
|
void QCPLayoutInset::updateLayout() { |
3129 |
|
|
for (int i = 0; i < mElements.size(); ++i) { |
3130 |
|
|
QRect insetRect; |
3131 |
|
|
QSize finalMinSize, finalMaxSize; |
3132 |
|
|
QSize minSizeHint = mElements.at(i)->minimumSizeHint(); |
3133 |
|
|
QSize maxSizeHint = mElements.at(i)->maximumSizeHint(); |
3134 |
|
|
finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 |
3135 |
|
|
? mElements.at(i)->minimumSize().width() |
3136 |
|
|
: minSizeHint.width()); |
3137 |
|
|
finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 |
3138 |
|
|
? mElements.at(i)->minimumSize().height() |
3139 |
|
|
: minSizeHint.height()); |
3140 |
|
|
finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < |
3141 |
|
|
QWIDGETSIZE_MAX |
3142 |
|
|
? mElements.at(i)->maximumSize().width() |
3143 |
|
|
: maxSizeHint.width()); |
3144 |
|
|
finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < |
3145 |
|
|
QWIDGETSIZE_MAX |
3146 |
|
|
? mElements.at(i)->maximumSize().height() |
3147 |
|
|
: maxSizeHint.height()); |
3148 |
|
|
if (mInsetPlacement.at(i) == ipFree) { |
3149 |
|
|
insetRect = QRect(rect().x() + rect().width() * mInsetRect.at(i).x(), |
3150 |
|
|
rect().y() + rect().height() * mInsetRect.at(i).y(), |
3151 |
|
|
rect().width() * mInsetRect.at(i).width(), |
3152 |
|
|
rect().height() * mInsetRect.at(i).height()); |
3153 |
|
|
if (insetRect.size().width() < finalMinSize.width()) |
3154 |
|
|
insetRect.setWidth(finalMinSize.width()); |
3155 |
|
|
if (insetRect.size().height() < finalMinSize.height()) |
3156 |
|
|
insetRect.setHeight(finalMinSize.height()); |
3157 |
|
|
if (insetRect.size().width() > finalMaxSize.width()) |
3158 |
|
|
insetRect.setWidth(finalMaxSize.width()); |
3159 |
|
|
if (insetRect.size().height() > finalMaxSize.height()) |
3160 |
|
|
insetRect.setHeight(finalMaxSize.height()); |
3161 |
|
|
} else if (mInsetPlacement.at(i) == ipBorderAligned) { |
3162 |
|
|
insetRect.setSize(finalMinSize); |
3163 |
|
|
Qt::Alignment al = mInsetAlignment.at(i); |
3164 |
|
|
if (al.testFlag(Qt::AlignLeft)) |
3165 |
|
|
insetRect.moveLeft(rect().x()); |
3166 |
|
|
else if (al.testFlag(Qt::AlignRight)) |
3167 |
|
|
insetRect.moveRight(rect().x() + rect().width()); |
3168 |
|
|
else |
3169 |
|
|
insetRect.moveLeft(rect().x() + rect().width() * 0.5 - |
3170 |
|
|
finalMinSize.width() * |
3171 |
|
|
0.5); // default to Qt::AlignHCenter |
3172 |
|
|
if (al.testFlag(Qt::AlignTop)) |
3173 |
|
|
insetRect.moveTop(rect().y()); |
3174 |
|
|
else if (al.testFlag(Qt::AlignBottom)) |
3175 |
|
|
insetRect.moveBottom(rect().y() + rect().height()); |
3176 |
|
|
else |
3177 |
|
|
insetRect.moveTop(rect().y() + rect().height() * 0.5 - |
3178 |
|
|
finalMinSize.height() * |
3179 |
|
|
0.5); // default to Qt::AlignVCenter |
3180 |
|
|
} |
3181 |
|
|
mElements.at(i)->setOuterRect(insetRect); |
3182 |
|
|
} |
3183 |
|
|
} |
3184 |
|
|
|
3185 |
|
|
/* inherits documentation from base class */ |
3186 |
|
|
int QCPLayoutInset::elementCount() const { return mElements.size(); } |
3187 |
|
|
|
3188 |
|
|
/* inherits documentation from base class */ |
3189 |
|
|
QCPLayoutElement *QCPLayoutInset::elementAt(int index) const { |
3190 |
|
|
if (index >= 0 && index < mElements.size()) |
3191 |
|
|
return mElements.at(index); |
3192 |
|
|
else |
3193 |
|
|
return 0; |
3194 |
|
|
} |
3195 |
|
|
|
3196 |
|
|
/* inherits documentation from base class */ |
3197 |
|
|
QCPLayoutElement *QCPLayoutInset::takeAt(int index) { |
3198 |
|
|
if (QCPLayoutElement *el = elementAt(index)) { |
3199 |
|
|
releaseElement(el); |
3200 |
|
|
mElements.removeAt(index); |
3201 |
|
|
mInsetPlacement.removeAt(index); |
3202 |
|
|
mInsetAlignment.removeAt(index); |
3203 |
|
|
mInsetRect.removeAt(index); |
3204 |
|
|
return el; |
3205 |
|
|
} else { |
3206 |
|
|
qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; |
3207 |
|
|
return 0; |
3208 |
|
|
} |
3209 |
|
|
} |
3210 |
|
|
|
3211 |
|
|
/* inherits documentation from base class */ |
3212 |
|
|
bool QCPLayoutInset::take(QCPLayoutElement *element) { |
3213 |
|
|
if (element) { |
3214 |
|
|
for (int i = 0; i < elementCount(); ++i) { |
3215 |
|
|
if (elementAt(i) == element) { |
3216 |
|
|
takeAt(i); |
3217 |
|
|
return true; |
3218 |
|
|
} |
3219 |
|
|
} |
3220 |
|
|
qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take"; |
3221 |
|
|
} else |
3222 |
|
|
qDebug() << Q_FUNC_INFO << "Can't take null element"; |
3223 |
|
|
return false; |
3224 |
|
|
} |
3225 |
|
|
|
3226 |
|
|
/*! |
3227 |
|
|
The inset layout is sensitive to events only at areas where its (visible) |
3228 |
|
|
child elements are sensitive. If the selectTest method of any of the child |
3229 |
|
|
elements returns a positive number for \a pos, this method returns a value |
3230 |
|
|
corresponding to 0.99 times the parent plot's selection tolerance. The inset |
3231 |
|
|
layout is not selectable itself by default. So if \a onlySelectable is true, |
3232 |
|
|
-1.0 is returned. |
3233 |
|
|
|
3234 |
|
|
See \ref QCPLayerable::selectTest for a general explanation of this virtual |
3235 |
|
|
method. |
3236 |
|
|
*/ |
3237 |
|
|
double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, |
3238 |
|
|
QVariant *details) const { |
3239 |
|
|
Q_UNUSED(details) |
3240 |
|
|
if (onlySelectable) return -1; |
3241 |
|
|
|
3242 |
|
|
for (int i = 0; i < mElements.size(); ++i) { |
3243 |
|
|
// inset layout shall only return positive selectTest, if actually an inset |
3244 |
|
|
// object is at pos else it would block the entire underlying QCPAxisRect |
3245 |
|
|
// with its surface. |
3246 |
|
|
if (mElements.at(i)->realVisibility() && |
3247 |
|
|
mElements.at(i)->selectTest(pos, onlySelectable) >= 0) |
3248 |
|
|
return mParentPlot->selectionTolerance() * 0.99; |
3249 |
|
|
} |
3250 |
|
|
return -1; |
3251 |
|
|
} |
3252 |
|
|
|
3253 |
|
|
/*! |
3254 |
|
|
Adds the specified \a element to the layout as an inset aligned at the border |
3255 |
|
|
(\ref setInsetAlignment is initialized with \ref ipBorderAligned). The |
3256 |
|
|
alignment is set to \a alignment. |
3257 |
|
|
|
3258 |
|
|
\a alignment is an or combination of the following alignment flags: |
3259 |
|
|
Qt::AlignLeft, Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, |
3260 |
|
|
Qt::AlignVCenter, Qt::AlignBottom. Any other alignment flags will be ignored. |
3261 |
|
|
|
3262 |
|
|
\see addElement(QCPLayoutElement *element, const QRectF &rect) |
3263 |
|
|
*/ |
3264 |
|
|
void QCPLayoutInset::addElement(QCPLayoutElement *element, |
3265 |
|
|
Qt::Alignment alignment) { |
3266 |
|
|
if (element) { |
3267 |
|
|
if (element->layout()) // remove from old layout first |
3268 |
|
|
element->layout()->take(element); |
3269 |
|
|
mElements.append(element); |
3270 |
|
|
mInsetPlacement.append(ipBorderAligned); |
3271 |
|
|
mInsetAlignment.append(alignment); |
3272 |
|
|
mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4)); |
3273 |
|
|
adoptElement(element); |
3274 |
|
|
} else |
3275 |
|
|
qDebug() << Q_FUNC_INFO << "Can't add null element"; |
3276 |
|
|
} |
3277 |
|
|
|
3278 |
|
|
/*! |
3279 |
|
|
Adds the specified \a element to the layout as an inset with free |
3280 |
|
|
positioning/sizing (\ref setInsetAlignment is initialized with \ref ipFree). |
3281 |
|
|
The position and size is set to \a rect. |
3282 |
|
|
|
3283 |
|
|
\a rect is given in fractions of the whole inset layout rect. So an inset with |
3284 |
|
|
rect (0, 0, 1, 1) will span the entire layout. An inset with rect (0.6, 0.1, |
3285 |
|
|
0.35, 0.35) will be in the top right corner of the layout, with 35% width and |
3286 |
|
|
height of the parent layout. |
3287 |
|
|
|
3288 |
|
|
\see addElement(QCPLayoutElement *element, Qt::Alignment alignment) |
3289 |
|
|
*/ |
3290 |
|
|
void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) { |
3291 |
|
|
if (element) { |
3292 |
|
|
if (element->layout()) // remove from old layout first |
3293 |
|
|
element->layout()->take(element); |
3294 |
|
|
mElements.append(element); |
3295 |
|
|
mInsetPlacement.append(ipFree); |
3296 |
|
|
mInsetAlignment.append(Qt::AlignRight | Qt::AlignTop); |
3297 |
|
|
mInsetRect.append(rect); |
3298 |
|
|
adoptElement(element); |
3299 |
|
|
} else |
3300 |
|
|
qDebug() << Q_FUNC_INFO << "Can't add null element"; |
3301 |
|
|
} |
3302 |
|
|
|
3303 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
3304 |
|
|
//////////////////// QCPLineEnding |
3305 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
3306 |
|
|
|
3307 |
|
|
/*! \class QCPLineEnding |
3308 |
|
|
\brief Handles the different ending decorations for line-like items |
3309 |
|
|
|
3310 |
|
|
\image html QCPLineEnding.png "The various ending styles currently supported" |
3311 |
|
|
|
3312 |
|
|
For every ending a line-like item has, an instance of this class exists. For |
3313 |
|
|
example, QCPItemLine has two endings which can be set with |
3314 |
|
|
QCPItemLine::setHead and QCPItemLine::setTail. |
3315 |
|
|
|
3316 |
|
|
The styles themselves are defined via the enum QCPLineEnding::EndingStyle. |
3317 |
|
|
Most decorations can be modified regarding width and length, see \ref setWidth |
3318 |
|
|
and \ref setLength. The direction of the ending decoration (e.g. direction an |
3319 |
|
|
arrow is pointing) is controlled by the line-like item. For example, when both |
3320 |
|
|
endings of a QCPItemLine are set to be arrows, they will point to opposite |
3321 |
|
|
directions, e.g. "outward". This can be changed by \ref setInverted, which |
3322 |
|
|
would make the respective arrow point inward. |
3323 |
|
|
|
3324 |
|
|
Note that due to the overloaded QCPLineEnding constructor, you may directly |
3325 |
|
|
specify a QCPLineEnding::EndingStyle where actually a QCPLineEnding is |
3326 |
|
|
expected, e.g. \snippet documentation/doc-code-snippets/mainwindow.cpp |
3327 |
|
|
qcplineending-sethead |
3328 |
|
|
*/ |
3329 |
|
|
|
3330 |
|
|
/*! |
3331 |
|
|
Creates a QCPLineEnding instance with default values (style \ref esNone). |
3332 |
|
|
*/ |
3333 |
|
|
QCPLineEnding::QCPLineEnding() |
3334 |
|
|
: mStyle(esNone), mWidth(8), mLength(10), mInverted(false) {} |
3335 |
|
|
|
3336 |
|
|
/*! |
3337 |
|
|
Creates a QCPLineEnding instance with the specified values. |
3338 |
|
|
*/ |
3339 |
|
|
QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, |
3340 |
|
|
double length, bool inverted) |
3341 |
|
|
: mStyle(style), mWidth(width), mLength(length), mInverted(inverted) {} |
3342 |
|
|
|
3343 |
|
|
/*! |
3344 |
|
|
Sets the style of the ending decoration. |
3345 |
|
|
*/ |
3346 |
|
|
void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style) { |
3347 |
|
|
mStyle = style; |
3348 |
|
|
} |
3349 |
|
|
|
3350 |
|
|
/*! |
3351 |
|
|
Sets the width of the ending decoration, if the style supports it. On arrows, |
3352 |
|
|
for example, the width defines the size perpendicular to the arrow's pointing |
3353 |
|
|
direction. |
3354 |
|
|
|
3355 |
|
|
\see setLength |
3356 |
|
|
*/ |
3357 |
|
|
void QCPLineEnding::setWidth(double width) { mWidth = width; } |
3358 |
|
|
|
3359 |
|
|
/*! |
3360 |
|
|
Sets the length of the ending decoration, if the style supports it. On arrows, |
3361 |
|
|
for example, the length defines the size in pointing direction. |
3362 |
|
|
|
3363 |
|
|
\see setWidth |
3364 |
|
|
*/ |
3365 |
|
|
void QCPLineEnding::setLength(double length) { mLength = length; } |
3366 |
|
|
|
3367 |
|
|
/*! |
3368 |
|
|
Sets whether the ending decoration shall be inverted. For example, an arrow |
3369 |
|
|
decoration will point inward when \a inverted is set to true. |
3370 |
|
|
|
3371 |
|
|
Note that also the \a width direction is inverted. For symmetrical ending |
3372 |
|
|
styles like arrows or discs, this doesn't make a difference. However, |
3373 |
|
|
asymmetric styles like \ref esHalfBar are affected by it, which can be used to |
3374 |
|
|
control to which side the half bar points to. |
3375 |
|
|
*/ |
3376 |
|
|
void QCPLineEnding::setInverted(bool inverted) { mInverted = inverted; } |
3377 |
|
|
|
3378 |
|
|
/*! \internal |
3379 |
|
|
|
3380 |
|
|
Returns the maximum pixel radius the ending decoration might cover, starting |
3381 |
|
|
from the position the decoration is drawn at (typically a line ending/\ref |
3382 |
|
|
QCPItemPosition of an item). |
3383 |
|
|
|
3384 |
|
|
This is relevant for clipping. Only omit painting of the decoration when the |
3385 |
|
|
position where the decoration is supposed to be drawn is farther away from the |
3386 |
|
|
clipping rect than the returned distance. |
3387 |
|
|
*/ |
3388 |
|
|
double QCPLineEnding::boundingDistance() const { |
3389 |
|
|
switch (mStyle) { |
3390 |
|
|
case esNone: |
3391 |
|
|
return 0; |
3392 |
|
|
|
3393 |
|
|
case esFlatArrow: |
3394 |
|
|
case esSpikeArrow: |
3395 |
|
|
case esLineArrow: |
3396 |
|
|
case esSkewedBar: |
3397 |
|
|
return qSqrt(mWidth * mWidth + |
3398 |
|
|
mLength * mLength); // items that have width and length |
3399 |
|
|
|
3400 |
|
|
case esDisc: |
3401 |
|
|
case esSquare: |
3402 |
|
|
case esDiamond: |
3403 |
|
|
case esBar: |
3404 |
|
|
case esHalfBar: |
3405 |
|
|
return mWidth * 1.42; // items that only have a width -> width*sqrt(2) |
3406 |
|
|
} |
3407 |
|
|
return 0; |
3408 |
|
|
} |
3409 |
|
|
|
3410 |
|
|
/*! |
3411 |
|
|
Starting from the origin of this line ending (which is style specific), |
3412 |
|
|
returns the length covered by the line ending symbol, in backward direction. |
3413 |
|
|
|
3414 |
|
|
For example, the \ref esSpikeArrow has a shorter real length than a \ref |
3415 |
|
|
esFlatArrow, even if both have the same \ref setLength value, because the |
3416 |
|
|
spike arrow has an inward curved back, which reduces the length along its |
3417 |
|
|
center axis (the drawing origin for arrows is at the tip). |
3418 |
|
|
|
3419 |
|
|
This function is used for precise, style specific placement of line endings, |
3420 |
|
|
for example in QCPAxes. |
3421 |
|
|
*/ |
3422 |
|
|
double QCPLineEnding::realLength() const { |
3423 |
|
|
switch (mStyle) { |
3424 |
|
|
case esNone: |
3425 |
|
|
case esLineArrow: |
3426 |
|
|
case esSkewedBar: |
3427 |
|
|
case esBar: |
3428 |
|
|
case esHalfBar: |
3429 |
|
|
return 0; |
3430 |
|
|
|
3431 |
|
|
case esFlatArrow: |
3432 |
|
|
return mLength; |
3433 |
|
|
|
3434 |
|
|
case esDisc: |
3435 |
|
|
case esSquare: |
3436 |
|
|
case esDiamond: |
3437 |
|
|
return mWidth * 0.5; |
3438 |
|
|
|
3439 |
|
|
case esSpikeArrow: |
3440 |
|
|
return mLength * 0.8; |
3441 |
|
|
} |
3442 |
|
|
return 0; |
3443 |
|
|
} |
3444 |
|
|
|
3445 |
|
|
/*! \internal |
3446 |
|
|
|
3447 |
|
|
Draws the line ending with the specified \a painter at the position \a pos. |
3448 |
|
|
The direction of the line ending is controlled with \a dir. |
3449 |
|
|
*/ |
3450 |
|
|
void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, |
3451 |
|
|
const QVector2D &dir) const { |
3452 |
|
|
if (mStyle == esNone) return; |
3453 |
|
|
|
3454 |
|
|
QVector2D lengthVec(dir.normalized()); |
3455 |
|
|
if (lengthVec.isNull()) lengthVec = QVector2D(1, 0); |
3456 |
|
|
QVector2D widthVec(-lengthVec.y(), lengthVec.x()); |
3457 |
|
|
lengthVec *= (float)(mLength * (mInverted ? -1 : 1)); |
3458 |
|
|
widthVec *= (float)(mWidth * 0.5 * (mInverted ? -1 : 1)); |
3459 |
|
|
|
3460 |
|
|
QPen penBackup = painter->pen(); |
3461 |
|
|
QBrush brushBackup = painter->brush(); |
3462 |
|
|
QPen miterPen = penBackup; |
3463 |
|
|
miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey |
3464 |
|
|
QBrush brush(painter->pen().color(), Qt::SolidPattern); |
3465 |
|
|
switch (mStyle) { |
3466 |
|
|
case esNone: |
3467 |
|
|
break; |
3468 |
|
|
case esFlatArrow: { |
3469 |
|
|
QPointF points[3] = {pos.toPointF(), |
3470 |
|
|
(pos - lengthVec + widthVec).toPointF(), |
3471 |
|
|
(pos - lengthVec - widthVec).toPointF()}; |
3472 |
|
|
painter->setPen(miterPen); |
3473 |
|
|
painter->setBrush(brush); |
3474 |
|
|
painter->drawConvexPolygon(points, 3); |
3475 |
|
|
painter->setBrush(brushBackup); |
3476 |
|
|
painter->setPen(penBackup); |
3477 |
|
|
break; |
3478 |
|
|
} |
3479 |
|
|
case esSpikeArrow: { |
3480 |
|
|
QPointF points[4] = {pos.toPointF(), |
3481 |
|
|
(pos - lengthVec + widthVec).toPointF(), |
3482 |
|
|
(pos - lengthVec * 0.8f).toPointF(), |
3483 |
|
|
(pos - lengthVec - widthVec).toPointF()}; |
3484 |
|
|
painter->setPen(miterPen); |
3485 |
|
|
painter->setBrush(brush); |
3486 |
|
|
painter->drawConvexPolygon(points, 4); |
3487 |
|
|
painter->setBrush(brushBackup); |
3488 |
|
|
painter->setPen(penBackup); |
3489 |
|
|
break; |
3490 |
|
|
} |
3491 |
|
|
case esLineArrow: { |
3492 |
|
|
QPointF points[3] = {(pos - lengthVec + widthVec).toPointF(), |
3493 |
|
|
pos.toPointF(), |
3494 |
|
|
(pos - lengthVec - widthVec).toPointF()}; |
3495 |
|
|
painter->setPen(miterPen); |
3496 |
|
|
painter->drawPolyline(points, 3); |
3497 |
|
|
painter->setPen(penBackup); |
3498 |
|
|
break; |
3499 |
|
|
} |
3500 |
|
|
case esDisc: { |
3501 |
|
|
painter->setBrush(brush); |
3502 |
|
|
painter->drawEllipse(pos.toPointF(), mWidth * 0.5, mWidth * 0.5); |
3503 |
|
|
painter->setBrush(brushBackup); |
3504 |
|
|
break; |
3505 |
|
|
} |
3506 |
|
|
case esSquare: { |
3507 |
|
|
QVector2D widthVecPerp(-widthVec.y(), widthVec.x()); |
3508 |
|
|
QPointF points[4] = {(pos - widthVecPerp + widthVec).toPointF(), |
3509 |
|
|
(pos - widthVecPerp - widthVec).toPointF(), |
3510 |
|
|
(pos + widthVecPerp - widthVec).toPointF(), |
3511 |
|
|
(pos + widthVecPerp + widthVec).toPointF()}; |
3512 |
|
|
painter->setPen(miterPen); |
3513 |
|
|
painter->setBrush(brush); |
3514 |
|
|
painter->drawConvexPolygon(points, 4); |
3515 |
|
|
painter->setBrush(brushBackup); |
3516 |
|
|
painter->setPen(penBackup); |
3517 |
|
|
break; |
3518 |
|
|
} |
3519 |
|
|
case esDiamond: { |
3520 |
|
|
QVector2D widthVecPerp(-widthVec.y(), widthVec.x()); |
3521 |
|
|
QPointF points[4] = { |
3522 |
|
|
(pos - widthVecPerp).toPointF(), (pos - widthVec).toPointF(), |
3523 |
|
|
(pos + widthVecPerp).toPointF(), (pos + widthVec).toPointF()}; |
3524 |
|
|
painter->setPen(miterPen); |
3525 |
|
|
painter->setBrush(brush); |
3526 |
|
|
painter->drawConvexPolygon(points, 4); |
3527 |
|
|
painter->setBrush(brushBackup); |
3528 |
|
|
painter->setPen(penBackup); |
3529 |
|
|
break; |
3530 |
|
|
} |
3531 |
|
|
case esBar: { |
3532 |
|
|
painter->drawLine((pos + widthVec).toPointF(), |
3533 |
|
|
(pos - widthVec).toPointF()); |
3534 |
|
|
break; |
3535 |
|
|
} |
3536 |
|
|
case esHalfBar: { |
3537 |
|
|
painter->drawLine((pos + widthVec).toPointF(), pos.toPointF()); |
3538 |
|
|
break; |
3539 |
|
|
} |
3540 |
|
|
case esSkewedBar: { |
3541 |
|
|
if (qFuzzyIsNull(painter->pen().widthF()) && |
3542 |
|
|
!painter->modes().testFlag(QCPPainter::pmNonCosmetic)) { |
3543 |
|
|
// if drawing with cosmetic pen (perfectly thin stroke, happens only in |
3544 |
|
|
// vector exports), draw bar exactly on tip of line |
3545 |
|
|
painter->drawLine( |
3546 |
|
|
(pos + widthVec + lengthVec * 0.2f * (mInverted ? -1 : 1)) |
3547 |
|
|
.toPointF(), |
3548 |
|
|
(pos - widthVec - lengthVec * 0.2f * (mInverted ? -1 : 1)) |
3549 |
|
|
.toPointF()); |
3550 |
|
|
} else { |
3551 |
|
|
// if drawing with thick (non-cosmetic) pen, shift bar a little in line |
3552 |
|
|
// direction to prevent line from sticking through bar slightly |
3553 |
|
|
painter->drawLine( |
3554 |
|
|
(pos + widthVec + lengthVec * 0.2f * (mInverted ? -1 : 1) + |
3555 |
|
|
dir.normalized() * qMax(1.0f, (float)painter->pen().widthF()) * |
3556 |
|
|
0.5f) |
3557 |
|
|
.toPointF(), |
3558 |
|
|
(pos - widthVec - lengthVec * 0.2f * (mInverted ? -1 : 1) + |
3559 |
|
|
dir.normalized() * qMax(1.0f, (float)painter->pen().widthF()) * |
3560 |
|
|
0.5f) |
3561 |
|
|
.toPointF()); |
3562 |
|
|
} |
3563 |
|
|
break; |
3564 |
|
|
} |
3565 |
|
|
} |
3566 |
|
|
} |
3567 |
|
|
|
3568 |
|
|
/*! \internal |
3569 |
|
|
\overload |
3570 |
|
|
|
3571 |
|
|
Draws the line ending. The direction is controlled with the \a angle parameter |
3572 |
|
|
in radians. |
3573 |
|
|
*/ |
3574 |
|
|
void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, |
3575 |
|
|
double angle) const { |
3576 |
|
|
draw(painter, pos, QVector2D(qCos(angle), qSin(angle))); |
3577 |
|
|
} |
3578 |
|
|
|
3579 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
3580 |
|
|
//////////////////// QCPGrid |
3581 |
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////// |
3582 |
|
|
|
3583 |
|
|
/*! \class QCPGrid |
3584 |
|
|
\brief Responsible for drawing the grid of a QCPAxis. |
3585 |
|
|
|
3586 |
|
|
This class is tightly bound to QCPAxis. Every axis owns a grid instance and |
3587 |
|
|
uses it to draw the grid lines, sub grid lines and zero-line. You can interact |
3588 |
|
|
with the grid of an axis via \ref QCPAxis::grid. Normally, you don't need to |
3589 |
|
|
create an instance of QCPGrid yourself. |
3590 |
|
|
|
3591 |
|
|
The axis and grid drawing was split into two classes to allow them to be |
3592 |
|
|
placed on different layers (both QCPAxis and QCPGrid inherit from |
3593 |
|
|
QCPLayerable). Thus it is possible to have the grid in the background and the |
3594 |
|
|
axes in the foreground, and any plottables/items in between. This described |
3595 |
|
|
situation is the default setup, see the QCPLayer documentation. |
3596 |
|
|
*/ |
3597 |
|
|
|
3598 |
|
|
/*! |
3599 |
|
|
Creates a QCPGrid instance and sets default values. |
3600 |
|
|
|
3601 |
|
|
You shouldn't instantiate grids on their own, since every QCPAxis brings its |
3602 |
|
|
own QCPGrid. |
3603 |
|
|
*/ |
3604 |
|
|
QCPGrid::QCPGrid(QCPAxis *parentAxis) |
3605 |
|
|
: QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis), |
3606 |
|
|
mParentAxis(parentAxis) { |
3607 |
|
|
// warning: this is called in QCPAxis constructor, so parentAxis members |
3608 |
|
|
// should not be accessed/called |
3609 |
|
|
setParent(parentAxis); |
3610 |
|
|
setPen(QPen(QColor(200, 200, 200), 0, Qt::DotLine)); |
3611 |
|
|
setSubGridPen(QPen(QColor(220, 220, 220), 0, Qt::DotLine)); |
3612 |
|
|
setZeroLinePen(QPen(QColor(200, 200, 200), 0, Qt::SolidLine)); |
3613 |
|
|
setSubGridVisible(false); |
3614 |
|
|
setAntialiased(false); |
3615 |
|
|
setAntialiasedSubGrid(false); |
3616 |
|
|
setAntialiasedZeroLine(false); |
3617 |
|
|
} |
3618 |
|
|
|
3619 |
|
|
/*! |
3620 |
|
|
Sets whether grid lines at sub tick marks are drawn. |
3621 |
|
|
|
3622 |
|
|
\see setSubGridPen |
3623 |
|
|
*/ |
3624 |
|
|
void QCPGrid::setSubGridVisible(bool visible) { mSubGridVisible = visible; } |
3625 |
|
|
|
3626 |
|
|
/*! |
3627 |
|
|
Sets whether sub grid lines are drawn antialiased. |
3628 |
|
|
*/ |
3629 |
|
|
void QCPGrid::setAntialiasedSubGrid(bool enabled) { |
3630 |
|
|
mAntialiasedSubGrid = enabled; |
3631 |
|
|
} |
3632 |
|
|
|
3633 |
|
|
/*! |
3634 |
|
|
Sets whether zero lines are drawn antialiased. |
3635 |
|
|
*/ |
3636 |
|
|
void QCPGrid::setAntialiasedZeroLine(bool enabled) { |
3637 |
|
|
mAntialiasedZeroLine = enabled; |
3638 |
|
|
} |
3639 |
|
|
|
3640 |
|
|
/*! |
3641 |
|
|
Sets the pen with which (major) grid lines are drawn. |
3642 |
|
|
*/ |
3643 |
|
|
void QCPGrid::setPen(const QPen &pen) { mPen = pen; } |
3644 |
|
|
|
3645 |
|
|
/*! |
3646 |
|
|
Sets the pen with which sub grid lines are drawn. |
3647 |
|
|
*/ |
3648 |
|
|
void QCPGrid::setSubGridPen(const QPen &pen) { mSubGridPen = pen; } |
3649 |
|
|
|
3650 |
|
|
/*! |
3651 |
|
|
Sets the pen with which zero lines are drawn. |
3652 |
|
|
|
3653 |
|
|
Zero lines are lines at value coordinate 0 which may be drawn with a different |
3654 |
|
|
pen than other grid lines. To disable zero lines and just draw normal grid |
3655 |
|
|
lines at zero, set \a pen to Qt::NoPen. |
3656 |
|
|
*/ |
3657 |
|
|
void QCPGrid::setZeroLinePen(const QPen &pen) { mZeroLinePen = pen; } |
3658 |
|
|
|
3659 |
|
|
/*! \internal |
3660 |
|
|
|
3661 |
|
|
A convenience function to easily set the QPainter::Antialiased hint on the |
3662 |
|
|
provided \a painter before drawing the major grid lines. |
3663 |
|
|
|
3664 |
|
|
This is the antialiasing state the painter passed to the \ref draw method is |
3665 |
|
|
in by default. |
3666 |
|
|
|
3667 |
|
|
This function takes into account the local setting of the antialiasing flag as |
3668 |
|
|
well as the overrides set with \ref QCustomPlot::setAntialiasedElements and |
3669 |
|
|
\ref QCustomPlot::setNotAntialiasedElements. |
3670 |
|
|
|
3671 |
|
|
\see setAntialiased |
3672 |
|
|
*/ |
3673 |
|
|
void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const { |
3674 |
|
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid); |
3675 |
|
|
} |
3676 |
|
|
|
3677 |
|
|
/*! \internal |
3678 |
|
|
|
3679 |
|
|
Draws grid lines and sub grid lines at the positions of (sub) ticks of the |
3680 |
|
|
parent axis, spanning over the complete axis rect. Also draws the zero line, |
3681 |
|
|
if appropriate (\ref setZeroLinePen). |
3682 |
|
|
*/ |
3683 |
|
|
void QCPGrid::draw(QCPPainter *painter) { |
3684 |
|
|
if (!mParentAxis) { |
3685 |
|
|
qDebug() << Q_FUNC_INFO << "invalid parent axis"; |
3686 |
|
|
return; |
3687 |
|
|
} |
3688 |
|
|
|
3689 |
|
|
if (mSubGridVisible) drawSubGridLines(painter); |
3690 |
|
|
drawGridLines(painter); |
3691 |
|
|
} |
3692 |
|
|
|
3693 |
|
|
/*! \internal |
3694 |
|
|
|
3695 |
|
|
Draws the main grid lines and possibly a zero line with the specified painter. |
3696 |
|
|
|
3697 |
|
|
This is a helper function called by \ref draw. |
3698 |
|
|
*/ |
3699 |
|
|
void QCPGrid::drawGridLines(QCPPainter *painter) const { |
3700 |
|
|
if (!mParentAxis) { |
3701 |
|
|
qDebug() << Q_FUNC_INFO << "invalid parent axis"; |
3702 |
|
|
return; |
3703 |
|
|
} |
3704 |
|
|
|
3705 |
|
|
int lowTick = mParentAxis->mLowestVisibleTick; |
3706 |
|
|
int highTick = mParentAxis->mHighestVisibleTick; |
3707 |
|
|
double t; // helper variable, result of coordinate-to-pixel transforms |
3708 |
|
|
if (mParentAxis->orientation() == Qt::Horizontal) { |
3709 |
|
|
// draw zeroline: |
3710 |
|
|
int zeroLineIndex = -1; |
3711 |
|
|
if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && |
3712 |
|
|
mParentAxis->mRange.upper > 0) { |
3713 |
|
|
applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); |
3714 |
|
|
painter->setPen(mZeroLinePen); |
3715 |
|
|
double epsilon = |
3716 |
|
|
mParentAxis->range().size() * 1E-6; // for comparing double to zero |
3717 |
|
|
for (int i = lowTick; i <= highTick; ++i) { |
3718 |
|
|
if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) { |
3719 |
|
|
zeroLineIndex = i; |
3720 |
|
|
t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x |
3721 |
|
|
painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, |
3722 |
|
|
mParentAxis->mAxisRect->top())); |
3723 |
|
|
break; |
3724 |
|
|
} |
3725 |
|
|
} |
3726 |
|
|
} |
3727 |
|
|
// draw grid lines: |
3728 |
|
|
applyDefaultAntialiasingHint(painter); |
3729 |
|
|
painter->setPen(mPen); |
3730 |
|
|
for (int i = lowTick; i <= highTick; ++i) { |
3731 |
|
|
if (i == zeroLineIndex) |
3732 |
|
|
continue; // don't draw a gridline on top of the zeroline |
3733 |
|
|
t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x |
3734 |
|
|
painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, |
3735 |
|
|
mParentAxis->mAxisRect->top())); |
3736 |
|
|
} |
3737 |
|
|
} else { |
3738 |
|
|
// draw zeroline: |
3739 |
|
|
int zeroLineIndex = -1; |
3740 |
|
|
if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && |
3741 |
|
|
mParentAxis->mRange.upper > 0) { |
3742 |
|
|
applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); |
3743 |
|
|
|