| 1 | // Copyright (C) 2022 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only | 
| 3 |  | 
| 4 | #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses | 
| 5 |  | 
| 6 | #include <QtCharts/QXYSeries> | 
| 7 | #include <QtCharts/QColorAxis> | 
| 8 | #include <private/qxyseries_p.h> | 
| 9 | #include <private/abstractdomain_p.h> | 
| 10 | #include <QtCharts/QValueAxis> | 
| 11 | #include <private/xychart_p.h> | 
| 12 | #include <QtCharts/QXYLegendMarker> | 
| 13 | #include <private/charthelpers_p.h> | 
| 14 | #include <private/qchart_p.h> | 
| 15 | #include <QtGui/QPainter> | 
| 16 | #include <QtMath> | 
| 17 |  | 
| 18 | QT_BEGIN_NAMESPACE | 
| 19 |  | 
| 20 | /*! | 
| 21 |     \class QXYSeries | 
| 22 |     \inmodule QtCharts | 
| 23 |     \brief The QXYSeries class is a base class for line, spline, and scatter | 
| 24 |     series. | 
| 25 |  | 
| 26 |     QXYSeries supports displaying best fit line on a chart. Best fit line is a line | 
| 27 |     through a chart that expresses the relationship between points. | 
| 28 | */ | 
| 29 | /*! | 
| 30 |     \qmltype XYSeries | 
| 31 |     \nativetype QXYSeries | 
| 32 |     \inqmlmodule QtCharts | 
| 33 |  | 
| 34 |     \inherits AbstractSeries | 
| 35 |  | 
| 36 |     \brief A base type for line, spline, and scatter series. | 
| 37 |  | 
| 38 |     XYSeries supports displaying best fit line on a chart. Best fit line is a line | 
| 39 |     through a chart that expresses the relationship between points. | 
| 40 | */ | 
| 41 |  | 
| 42 | /*! | 
| 43 |     \enum QXYSeries::PointConfiguration | 
| 44 |  | 
| 45 |     This enum value describes the particular configuration of a point. | 
| 46 |  | 
| 47 |     \value Color | 
| 48 |            This enum value can be used to change a point's color. If used together | 
| 49 |            with QXYSeries::setPointConfiguration, the configuration's value should | 
| 50 |            be a valid QColor. | 
| 51 |     \value Size | 
| 52 |            This enum value can be used to change a point's size. If used together | 
| 53 |            with QXYSeries::setPointConfiguration, the configuration's value should | 
| 54 |            be a number, such as \c qreal or \c int. | 
| 55 |     \value Visibility | 
| 56 |            This enum value can be used to hide or show the point. If used | 
| 57 |            together with QXYSeries::setPointConfiguration, the configuration's value | 
| 58 |            should be boolean. | 
| 59 |     \value LabelVisibility | 
| 60 |            This enum value can be used to hide or show the label of the point. If used | 
| 61 |            together with QXYSeries::setPointConfiguration, the configuration's value | 
| 62 |            should be boolean. | 
| 63 |     \value [since 6.5] LabelFormat | 
| 64 |            This enum value can be used to set custom label text per-point. If used together with | 
| 65 |            QXYSeries::setPointConfiguration, the configuration's value should be a string. | 
| 66 |            \note If an empty string is set as the LabelFormat, it will be ignored, and the series | 
| 67 |            pointLabelsFormat will be used. | 
| 68 |            \sa pointLabelsFormat | 
| 69 |  | 
| 70 |     \sa setPointConfiguration() | 
| 71 |     \since 6.2 | 
| 72 | */ | 
| 73 |  | 
| 74 | /*! | 
| 75 |     \qmlproperty AbstractAxis XYSeries::axisX | 
| 76 |     The x-axis used for the series. If you leave both axisX and axisXTop | 
| 77 |     undefined, a value axis is created for the series. | 
| 78 |     \sa axisXTop, ValueAxis | 
| 79 | */ | 
| 80 |  | 
| 81 | /*! | 
| 82 |     \qmlproperty AbstractAxis XYSeries::axisY | 
| 83 |     The y-axis used for the series. If you leave both axisY and axisYRight | 
| 84 |     undefined, a value axis is created for the series. | 
| 85 |     \sa axisYRight, ValueAxis | 
| 86 | */ | 
| 87 |  | 
| 88 | /*! | 
| 89 |     \qmlproperty AbstractAxis XYSeries::axisXTop | 
| 90 |     The x-axis used for the series, drawn on top of the chart view. | 
| 91 |  | 
| 92 |     \note You can only provide either axisX or axisXTop, not both. | 
| 93 |     \sa axisX | 
| 94 | */ | 
| 95 |  | 
| 96 | /*! | 
| 97 |     \qmlproperty AbstractAxis XYSeries::axisYRight | 
| 98 |     The y-axis used for the series, drawn to the right on the chart view. | 
| 99 |  | 
| 100 |     \note You can only provide either axisY or axisYRight, not both. | 
| 101 |     \sa axisY | 
| 102 | */ | 
| 103 |  | 
| 104 | /*! | 
| 105 |     \qmlproperty AbstractAxis XYSeries::axisAngular | 
| 106 |     The angular axis used for the series, drawn around the polar chart view. | 
| 107 |     \sa axisX | 
| 108 | */ | 
| 109 |  | 
| 110 | /*! | 
| 111 |     \qmlproperty AbstractAxis XYSeries::axisRadial | 
| 112 |     The radial axis used for the series, drawn inside the polar chart view. | 
| 113 |     \sa axisY | 
| 114 | */ | 
| 115 |  | 
| 116 | /*! | 
| 117 |     \property QXYSeries::pointsVisible | 
| 118 |     \brief Whether the data points are visible and should be drawn. | 
| 119 | */ | 
| 120 | /*! | 
| 121 |     \qmlproperty bool XYSeries::pointsVisible | 
| 122 |     Whether the data points are visible and should be drawn. | 
| 123 | */ | 
| 124 |  | 
| 125 | /*! | 
| 126 |    \fn QPen QXYSeries::pen() const | 
| 127 |    Returns the pen used to draw the outline of the data points for the series. | 
| 128 |     \sa setPen() | 
| 129 | */ | 
| 130 |  | 
| 131 | /*! | 
| 132 |    \fn QBrush QXYSeries::brush() const | 
| 133 |    Returns the brush used to fill the data points for the series. | 
| 134 |     \sa setBrush() | 
| 135 | */ | 
| 136 |  | 
| 137 | /*! | 
| 138 |     \property QXYSeries::color | 
| 139 |     \brief The color of the series. | 
| 140 |  | 
| 141 |     This is the line (pen) color in case of QLineSeries or QSplineSeries and the | 
| 142 |     fill (brush) color in case of QScatterSeries or QAreaSeries. | 
| 143 |     \sa pen(), brush() | 
| 144 | */ | 
| 145 | /*! | 
| 146 |     \qmlproperty color XYSeries::color | 
| 147 |     The color of the series. This is the line (pen) color in case of LineSeries | 
| 148 |     or SplineSeries and the fill (brush) color in case of ScatterSeries or | 
| 149 |     AreaSeries. | 
| 150 | */ | 
| 151 |  | 
| 152 | /*! | 
| 153 |     \property QXYSeries::pointLabelsFormat | 
| 154 |     \brief The format used for showing labels with data points. | 
| 155 |  | 
| 156 |     QXYSeries supports the following format tags: | 
| 157 |     \table | 
| 158 |       \row | 
| 159 |         \li @index       \li The index in the series of the data point. [since 6.5] | 
| 160 |       \row | 
| 161 |         \li @xPoint      \li The x-coordinate of the data point. | 
| 162 |       \row | 
| 163 |         \li @yPoint      \li The y-coordinate of the data point. | 
| 164 |     \endtable | 
| 165 |  | 
| 166 |     For example, the following usage of the format tags would produce labels | 
| 167 |     that display the data point shown inside brackets separated by a comma | 
| 168 |     (x, y): | 
| 169 |  | 
| 170 |     \code | 
| 171 |     series->setPointLabelsFormat("@index: (@xPoint, @yPoint)"); | 
| 172 |     \endcode | 
| 173 |  | 
| 174 |     By default, the labels' format is set to \c {@xPoint, @yPoint}. The labels | 
| 175 |     are shown on the plot area, and the labels on the edge of the plot area are | 
| 176 |     cut. If the points are close to each other, the labels may overlap. | 
| 177 |  | 
| 178 |     \sa pointLabelsVisible, pointLabelsFont, pointLabelsColor | 
| 179 | */ | 
| 180 | /*! | 
| 181 |     \qmlproperty string XYSeries::pointLabelsFormat | 
| 182 |     The format used for showing labels with data points. | 
| 183 |  | 
| 184 |     \sa pointLabelsVisible, pointLabelsFont, pointLabelsColor | 
| 185 | */ | 
| 186 | /*! | 
| 187 |     \fn void QXYSeries::pointLabelsFormatChanged(const QString &format) | 
| 188 |     This signal is emitted when the format of data point labels changes to | 
| 189 |     \a format. | 
| 190 | */ | 
| 191 |  | 
| 192 | /*! | 
| 193 |     \property QXYSeries::pointLabelsVisible | 
| 194 |     \brief The visibility of data point labels. | 
| 195 |  | 
| 196 |     This property is \c false by default. | 
| 197 |  | 
| 198 |     \sa pointLabelsFormat, pointLabelsClipping | 
| 199 | */ | 
| 200 | /*! | 
| 201 |     \qmlproperty bool XYSeries::pointLabelsVisible | 
| 202 |     The visibility of data point labels. This property is \c false by default. | 
| 203 |  | 
| 204 |     \sa pointLabelsFormat, pointLabelsClipping | 
| 205 | */ | 
| 206 | /*! | 
| 207 |     \fn void QXYSeries::pointLabelsVisibilityChanged(bool visible) | 
| 208 |     This signal is emitted when the visibility of the data point labels | 
| 209 |     changes to \a visible. | 
| 210 | */ | 
| 211 |  | 
| 212 | /*! | 
| 213 |     \property QXYSeries::pointLabelsFont | 
| 214 |     \brief The font used for data point labels. | 
| 215 |  | 
| 216 |     \sa pointLabelsFormat | 
| 217 | */ | 
| 218 | /*! | 
| 219 |     \qmlproperty font XYSeries::pointLabelsFont | 
| 220 |     The font used for data point labels. | 
| 221 |  | 
| 222 |     \sa pointLabelsFormat | 
| 223 | */ | 
| 224 | /*! | 
| 225 |     \fn void QXYSeries::pointLabelsFontChanged(const QFont &font); | 
| 226 |     This signal is emitted when the font used for data point labels changes to | 
| 227 |     \a font. | 
| 228 | */ | 
| 229 |  | 
| 230 | /*! | 
| 231 |     \property QXYSeries::pointLabelsColor | 
| 232 |     \brief The color used for data point labels. By default, the color is the color of the brush | 
| 233 |     defined in theme for labels. | 
| 234 |  | 
| 235 |     \sa pointLabelsFormat | 
| 236 | */ | 
| 237 | /*! | 
| 238 |     \qmlproperty font XYSeries::pointLabelsColor | 
| 239 |     The color used for data point labels. By default, the color is the color of the brush | 
| 240 |     defined in theme for labels. | 
| 241 |  | 
| 242 |     \sa pointLabelsFormat | 
| 243 | */ | 
| 244 | /*! | 
| 245 |     \fn void QXYSeries::pointLabelsColorChanged(const QColor &color); | 
| 246 |     This signal is emitted when the color used for data point labels changes to | 
| 247 |     \a color. | 
| 248 | */ | 
| 249 |  | 
| 250 | /*! | 
| 251 |     \property QXYSeries::pointLabelsClipping | 
| 252 |     \brief The clipping for data point labels. | 
| 253 |  | 
| 254 |     This property is \c true by default. The labels on the edge of the plot area | 
| 255 |     are cut when clipping is enabled. | 
| 256 |  | 
| 257 |     \sa pointLabelsVisible | 
| 258 | */ | 
| 259 | /*! | 
| 260 |     \qmlproperty bool XYSeries::pointLabelsClipping | 
| 261 |     The clipping for data point labels. This property is \c true by default. The | 
| 262 |     labels on the edge of the plot area are cut when clipping is enabled. | 
| 263 |  | 
| 264 |     \sa pointLabelsVisible | 
| 265 | */ | 
| 266 |  | 
| 267 | /*! | 
| 268 |     \property QXYSeries::selectedColor | 
| 269 |     \brief The color of the selected points. | 
| 270 |  | 
| 271 |     This is the fill (brush) color of points marked as selected. If not specified, | 
| 272 |     value of QXYSeries::color is used as default. | 
| 273 |     \sa color | 
| 274 |     \since 6.2 | 
| 275 | */ | 
| 276 | /*! | 
| 277 |     \qmlproperty color XYSeries::selectedColor | 
| 278 |     The color of the selected points. This is the fill (brush) color of points marked | 
| 279 |     as selected. | 
| 280 |     If not specified, value of QXYSeries::color is used as default. | 
| 281 |     \sa color | 
| 282 | */ | 
| 283 |  | 
| 284 | /*! | 
| 285 |     \fn void QXYSeries::markerSizeChanged(qreal size) | 
| 286 |     This signal is emitted when the marker size changes to \a size. | 
| 287 | */ | 
| 288 |  | 
| 289 | /*! | 
| 290 |     \fn void QXYSeries::pointLabelsClippingChanged(bool clipping) | 
| 291 |     This signal is emitted when the clipping of the data point labels changes to | 
| 292 |     \a clipping. | 
| 293 | */ | 
| 294 | /*! | 
| 295 |     \property QXYSeries::bestFitLineVisible | 
| 296 |     \brief The visibility of the best fit line. | 
| 297 |  | 
| 298 |     This property is \c false by default. | 
| 299 |  | 
| 300 |     \sa bestFitLineEquation | 
| 301 |     \since 6.2 | 
| 302 | */ | 
| 303 | /*! | 
| 304 |     \qmlproperty bool XYSeries::bestFitLineVisible | 
| 305 |     The visibility of the best fit line. | 
| 306 |     This property is \c false by default. | 
| 307 | */ | 
| 308 | /*! | 
| 309 |     \fn void QXYSeries::bestFitLineVisibilityChanged(bool visible) | 
| 310 |     This signal is emitted when the visibility of the best fit line changes to | 
| 311 |     \a visible. | 
| 312 | */ | 
| 313 | /*! | 
| 314 |     \property QXYSeries::bestFitLineColor | 
| 315 |     \brief The color of best fit line. | 
| 316 |  | 
| 317 |     \sa bestFitLineEquation, bestFitLineVisible | 
| 318 |     \since 6.2 | 
| 319 | */ | 
| 320 | /*! | 
| 321 |     \qmlproperty color XYSeries::bestFitLineColor | 
| 322 |     The color of best fit line. | 
| 323 |     \sa bestFitLineVisible | 
| 324 | */ | 
| 325 | /*! | 
| 326 |     \fn void QXYSeries::bestFitLineColorChanged(const QColor &color) | 
| 327 |     This signal is emitted when the color used for the best fit line changes to | 
| 328 |     \a color. | 
| 329 | */ | 
| 330 |  | 
| 331 | /*! | 
| 332 |     \fn void QXYSeries::clicked(const QPointF& point) | 
| 333 |     This signal is emitted when the user triggers a mouse event by | 
| 334 |     clicking the point \a point in the chart. | 
| 335 |  | 
| 336 |     \sa pressed(), released(), doubleClicked() | 
| 337 | */ | 
| 338 | /*! | 
| 339 |     \qmlsignal XYSeries::clicked(point point) | 
| 340 |     This signal is emitted when the user triggers a mouse event by clicking the | 
| 341 |     point \a point in the chart. For example: | 
| 342 |     \code | 
| 343 |     LineSeries { | 
| 344 |         XYPoint { x: 0; y: 0 } | 
| 345 |         XYPoint { x: 1.1; y: 2.1 } | 
| 346 |         onClicked: console.log("onClicked: " + point.x + ", " + point.y); | 
| 347 |     } | 
| 348 |     \endcode | 
| 349 |  | 
| 350 |     The corresponding signal handler is \c onClicked(). | 
| 351 |  | 
| 352 |     \sa pressed(), released(), doubleClicked() | 
| 353 | */ | 
| 354 |  | 
| 355 | /*! | 
| 356 |     \fn void QXYSeries::hovered(const QPointF &point, bool state) | 
| 357 |     This signal is emitted when a mouse is hovered over the point \a point in | 
| 358 |     the chart. When the mouse moves over the point, \a state turns \c true, | 
| 359 |     and when the mouse moves away again, it turns \c false. | 
| 360 | */ | 
| 361 | /*! | 
| 362 |     \qmlsignal XYSeries::hovered(point point, bool state) | 
| 363 |     This signal is emitted when a mouse is hovered over the point \a point in | 
| 364 |     the chart. When the mouse moves over the point, \a state turns \c true, | 
| 365 |     and when the mouse moves away again, it turns \c false. | 
| 366 |  | 
| 367 |     The corresponding signal handler is \c onHovered(). | 
| 368 | */ | 
| 369 |  | 
| 370 | /*! | 
| 371 |     \fn void QXYSeries::pressed(const QPointF& point) | 
| 372 |     This signal is emitted when the user presses the data point \a point in the | 
| 373 |     chart and holds down the mouse button. | 
| 374 |  | 
| 375 |     \sa clicked(), released(), doubleClicked() | 
| 376 | */ | 
| 377 | /*! | 
| 378 |     \qmlsignal XYSeries::pressed(point point) | 
| 379 |     This signal is emitted when the user presses the data point \a point in the | 
| 380 |     chart and holds down the mouse button. For example: | 
| 381 |     \code | 
| 382 |     LineSeries { | 
| 383 |         XYPoint { x: 0; y: 0 } | 
| 384 |         XYPoint { x: 1.1; y: 2.1 } | 
| 385 |         onPressed: console.log("onPressed: " + point.x + ", " + point.y); | 
| 386 |     } | 
| 387 |     \endcode | 
| 388 |  | 
| 389 |     The corresponding signal handler is \c onPressed(). | 
| 390 |  | 
| 391 |     \sa clicked(), released(), doubleClicked() | 
| 392 | */ | 
| 393 |  | 
| 394 | /*! | 
| 395 |     \fn void QXYSeries::released(const QPointF& point) | 
| 396 |     This signal is emitted when the user releases the mouse press on the data | 
| 397 |     point specified by \a point. | 
| 398 |     \sa pressed(), clicked(), doubleClicked() | 
| 399 | */ | 
| 400 | /*! | 
| 401 |     \qmlsignal XYSeries::released(point point) | 
| 402 |     This signal is emitted when the user releases the mouse press on the data | 
| 403 |     point specified by \a point. | 
| 404 |     For example: | 
| 405 |     \code | 
| 406 |     LineSeries { | 
| 407 |         XYPoint { x: 0; y: 0 } | 
| 408 |         XYPoint { x: 1.1; y: 2.1 } | 
| 409 |         onReleased: console.log("onReleased: " + point.x + ", " + point.y); | 
| 410 |     } | 
| 411 |     \endcode | 
| 412 |  | 
| 413 |     The corresponding signal handler is \c onReleased(). | 
| 414 |  | 
| 415 |     \sa pressed(), clicked(), doubleClicked() | 
| 416 | */ | 
| 417 |  | 
| 418 | /*! | 
| 419 |     \fn void QXYSeries::doubleClicked(const QPointF& point) | 
| 420 |     This signal is emitted when the user double-clicks the data point \a point | 
| 421 |     in the chart. The \a point is the point where the first press was triggered. | 
| 422 |     \sa pressed(), released(), clicked() | 
| 423 | */ | 
| 424 | /*! | 
| 425 |     \qmlsignal XYSeries::doubleClicked(point point) | 
| 426 |     This signal is emitted when the user double-clicks the data point \a point | 
| 427 |     in the chart. The \a point is the point where the first press was triggered. | 
| 428 |     For example: | 
| 429 |     \code | 
| 430 |     LineSeries { | 
| 431 |         XYPoint { x: 0; y: 0 } | 
| 432 |         XYPoint { x: 1.1; y: 2.1 } | 
| 433 |         onDoubleClicked: console.log("onDoubleClicked: " + point.x + ", " + point.y); | 
| 434 |     } | 
| 435 |     \endcode | 
| 436 |  | 
| 437 |     The corresponding signal handler is \c onDoubleClicked(). | 
| 438 |  | 
| 439 |     \sa pressed(), released(), clicked() | 
| 440 | */ | 
| 441 |  | 
| 442 | /*! | 
| 443 |     \fn void QXYSeries::pointReplaced(int index) | 
| 444 |     This signal is emitted when a point is replaced at the position specified by | 
| 445 |     \a index. | 
| 446 |     \sa replace() | 
| 447 | */ | 
| 448 | /*! | 
| 449 |     \qmlsignal XYSeries::pointReplaced(int index) | 
| 450 |     This signal is emitted when a point is replaced at the position specified by | 
| 451 |     \a index. | 
| 452 |  | 
| 453 |     The corresponding signal handler is \c onPointReplaced(). | 
| 454 | */ | 
| 455 |  | 
| 456 | /*! | 
| 457 |     \fn void QXYSeries::pointsReplaced() | 
| 458 |     This signal is emitted when all points are replaced with other points. | 
| 459 |     \sa replace() | 
| 460 | */ | 
| 461 | /*! | 
| 462 |     \qmlsignal XYSeries::pointsReplaced() | 
| 463 |     This signal is emitted when all points are replaced with other points. | 
| 464 |  | 
| 465 |     The corresponding signal handler is \c onPointsReplaced(). | 
| 466 | */ | 
| 467 |  | 
| 468 | /*! | 
| 469 |     \fn void QXYSeries::pointAdded(int index) | 
| 470 |     This signal is emitted when a point is added at the position specified by | 
| 471 |     \a index. | 
| 472 |     \sa append(), insert() | 
| 473 | */ | 
| 474 | /*! | 
| 475 |     \qmlsignal XYSeries::pointAdded(int index) | 
| 476 |     This signal is emitted when a point is added at the position specified by | 
| 477 |     \a index. | 
| 478 |  | 
| 479 |     The corresponding signal handler is \c onPointAdded(). | 
| 480 | */ | 
| 481 |  | 
| 482 | /*! | 
| 483 |     \fn void QXYSeries::pointRemoved(int index) | 
| 484 |     This signal is emitted when a point is removed from the position specified | 
| 485 |     by \a index. | 
| 486 |     \sa remove() | 
| 487 | */ | 
| 488 |  | 
| 489 | /*! | 
| 490 |     \qmlsignal XYSeries::pointRemoved(int index) | 
| 491 |     This signal is emitted when a point is removed from the position specified | 
| 492 |     by \a index. | 
| 493 |  | 
| 494 |     The corresponding signal handler is \c onPointRemoved(). | 
| 495 | */ | 
| 496 |  | 
| 497 | /*! | 
| 498 |     \fn void QXYSeries::pointsRemoved(int index, int count) | 
| 499 |     This signal is emitted when the number of points specified by \a count | 
| 500 |     is removed starting at the position specified by \a index. | 
| 501 |     \sa removePoints(), clear() | 
| 502 | */ | 
| 503 |  | 
| 504 | /*! | 
| 505 |     \qmlsignal XYSeries::pointsRemoved(int index, int count) | 
| 506 |     This signal is emitted when the number of points specified by \a count | 
| 507 |     is removed starting at the position specified by \a index. | 
| 508 |  | 
| 509 |     The corresponding signal handler is \c onPointRemoved(). | 
| 510 | */ | 
| 511 |  | 
| 512 | /*! | 
| 513 |     \fn void QXYSeries::colorChanged(QColor color) | 
| 514 |     This signal is emitted when the line (pen) color changes to \a color. | 
| 515 | */ | 
| 516 |  | 
| 517 | /*! | 
| 518 |     \fn void QXYSeries::penChanged(const QPen &pen) | 
| 519 |     This signal is emitted when the pen changes to \a pen. | 
| 520 | */ | 
| 521 |  | 
| 522 | /*! | 
| 523 |     \fn void QXYSeries::selectedPointsChanged() | 
| 524 |     This signal is emitted when the points selection changes. | 
| 525 | */ | 
| 526 |  | 
| 527 | /*! | 
| 528 |     \fn void QXYSeries::lightMarkerChanged(const QImage &lightMarker) | 
| 529 |     This signal is emitted when the light marker image changes to \a lightMarker. | 
| 530 |     \sa QXYSeries::setLightMarker() | 
| 531 |     \since 6.2 | 
| 532 | */ | 
| 533 |  | 
| 534 | /*! | 
| 535 |     \fn void QXYSeriesPrivate::seriesUpdated() | 
| 536 |     \internal | 
| 537 | */ | 
| 538 |  | 
| 539 | /*! | 
| 540 |     \qmlmethod XYSeries::append(real x, real y) | 
| 541 |     Appends a point with the coordinates \a x and \a y to the series. | 
| 542 | */ | 
| 543 |  | 
| 544 | /*! | 
| 545 |     \qmlmethod XYSeries::replace(real oldX, real oldY, real newX, real newY) | 
| 546 |     Replaces the point with the coordinates \a oldX and \a oldY with the point | 
| 547 |     with the coordinates \a newX and \a newY. Does nothing if the old point does | 
| 548 |     not exist. | 
| 549 | */ | 
| 550 |  | 
| 551 | /*! | 
| 552 |     \qmlmethod XYSeries::remove(real x, real y) | 
| 553 |     Removes the point with the coordinates \a x and \a y from the series. Does | 
| 554 |     nothing if the point does not exist. | 
| 555 | */ | 
| 556 |  | 
| 557 | /*! | 
| 558 |     \qmlmethod XYSeries::remove(int index) | 
| 559 |     Removes the point at the position specified by \a index from the series. | 
| 560 | */ | 
| 561 |  | 
| 562 | /*! | 
| 563 |     \qmlmethod XYSeries::removePoints(int index, int count) | 
| 564 |     Removes the number of points specified by \a count from the series starting | 
| 565 |     at the position specified by \a index. | 
| 566 | */ | 
| 567 |  | 
| 568 | /*! | 
| 569 |     \qmlmethod XYSeries::insert(int index, real x, real y) | 
| 570 |     Inserts a point with the coordinates \a x and \a y to the position specified | 
| 571 |     by \a index in the series. If the index is 0 or less than 0, the point is | 
| 572 |     prepended to the list of points. If the index is equal to or greater than | 
| 573 |     than the number of points in the series, the point is appended to the | 
| 574 |     list of points. | 
| 575 | */ | 
| 576 |  | 
| 577 | /*! | 
| 578 |     \qmlmethod QPointF XYSeries::at(int index) | 
| 579 |     Returns the point at the position specified by \a index. Returns (0, 0) if | 
| 580 |     the index is not valid. | 
| 581 | */ | 
| 582 |  | 
| 583 | /*! | 
| 584 |     \internal | 
| 585 |  | 
| 586 |     Constructs an empty series object that is a child of \a parent. | 
| 587 |     When the series object is added to QChart, instance ownerships is transferred. | 
| 588 | */ | 
| 589 | QXYSeries::QXYSeries(QXYSeriesPrivate &d, QObject *parent) | 
| 590 |     : QAbstractSeries(d, parent) | 
| 591 | { | 
| 592 | } | 
| 593 |  | 
| 594 | /*! | 
| 595 |     Deletes the series. Series added to QChart instances are owned by them, | 
| 596 |     and are deleted when the QChart instances are deleted. | 
| 597 | */ | 
| 598 | QXYSeries::~QXYSeries() | 
| 599 | { | 
| 600 | } | 
| 601 |  | 
| 602 | /*! | 
| 603 |     Adds the data point with the coordinates \a x and \a y to the series. | 
| 604 |  */ | 
| 605 | void QXYSeries::append(qreal x, qreal y) | 
| 606 | { | 
| 607 |     append(point: QPointF(x, y)); | 
| 608 | } | 
| 609 |  | 
| 610 | /*! | 
| 611 |    \overload | 
| 612 |    Adds the data point \a point to the series. | 
| 613 |  */ | 
| 614 | void QXYSeries::append(const QPointF &point) | 
| 615 | { | 
| 616 |     Q_D(QXYSeries); | 
| 617 |  | 
| 618 |     if (isValidValue(point)) { | 
| 619 |         d->m_points << point; | 
| 620 |         emit pointAdded(index: d->m_points.size() - 1); | 
| 621 |     } | 
| 622 | } | 
| 623 |  | 
| 624 | /*! | 
| 625 |    \overload | 
| 626 |    Adds the list of data points specified by \a points to the series. | 
| 627 |  */ | 
| 628 | void QXYSeries::append(const QList<QPointF> &points) | 
| 629 | { | 
| 630 |     foreach (const QPointF &point , points) | 
| 631 |         append(point); | 
| 632 | } | 
| 633 |  | 
| 634 | /*! | 
| 635 |     Replaces the point with the coordinates \a oldX and \a oldY with the point | 
| 636 |     with the coordinates \a newX and \a newY. Does nothing if the old point does | 
| 637 |     not exist. | 
| 638 |  | 
| 639 |     \sa pointReplaced() | 
| 640 | */ | 
| 641 | void QXYSeries::replace(qreal oldX, qreal oldY, qreal newX, qreal newY) | 
| 642 | { | 
| 643 |     replace(oldPoint: QPointF(oldX, oldY), newPoint: QPointF(newX, newY)); | 
| 644 | } | 
| 645 |  | 
| 646 | /*! | 
| 647 |   Replaces the point specified by \a oldPoint with the one specified by | 
| 648 |   \a newPoint. | 
| 649 |   \sa pointReplaced() | 
| 650 | */ | 
| 651 | void QXYSeries::replace(const QPointF &oldPoint, const QPointF &newPoint) | 
| 652 | { | 
| 653 |     Q_D(QXYSeries); | 
| 654 |     int index = d->m_points.indexOf(t: oldPoint); | 
| 655 |     if (index == -1) | 
| 656 |         return; | 
| 657 |     replace(index, newPoint); | 
| 658 | } | 
| 659 |  | 
| 660 | /*! | 
| 661 |   Replaces the point at the position specified by \a index with the point that | 
| 662 |   has the coordinates \a newX and \a newY. | 
| 663 |   \sa pointReplaced() | 
| 664 | */ | 
| 665 | void QXYSeries::replace(int index, qreal newX, qreal newY) | 
| 666 | { | 
| 667 |     replace(index, newPoint: QPointF(newX, newY)); | 
| 668 | } | 
| 669 |  | 
| 670 | /*! | 
| 671 |   Replaces the point at the position specified by \a index with the point | 
| 672 |   specified by \a newPoint. | 
| 673 |   \sa pointReplaced() | 
| 674 | */ | 
| 675 | void QXYSeries::replace(int index, const QPointF &newPoint) | 
| 676 | { | 
| 677 |     Q_D(QXYSeries); | 
| 678 |     if (isValidValue(point: newPoint)) { | 
| 679 |         d->m_points[index] = newPoint; | 
| 680 |         emit pointReplaced(index); | 
| 681 |     } | 
| 682 | } | 
| 683 |  | 
| 684 | /*! | 
| 685 |   Replaces the current points with the points specified by \a points. | 
| 686 |   \note This is much faster than replacing data points one by one, | 
| 687 |   or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced() | 
| 688 |   when the points have been replaced. | 
| 689 |   \sa pointsReplaced() | 
| 690 | */ | 
| 691 | void QXYSeries::replace(const QList<QPointF> &points) | 
| 692 | { | 
| 693 |     Q_D(QXYSeries); | 
| 694 |     d->m_points = points; | 
| 695 |     emit pointsReplaced(); | 
| 696 | } | 
| 697 |  | 
| 698 | /*! | 
| 699 |     Removes the configuration of a point located at \a index | 
| 700 |     and restores the default look derived from the series' settings. | 
| 701 |  | 
| 702 |     \note It doesn't affect the configuration of other points. | 
| 703 |     \sa clearPointsConfiguration(), setPointConfiguration() | 
| 704 |     \since 6.2 | 
| 705 | */ | 
| 706 | void QXYSeries::clearPointConfiguration(const int index) | 
| 707 | { | 
| 708 |     Q_D(QXYSeries); | 
| 709 |     if (d->m_pointsConfiguration.contains(key: index)) { | 
| 710 |         d->m_pointsConfiguration.remove(key: index); | 
| 711 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 712 |     } | 
| 713 | } | 
| 714 |  | 
| 715 | /*! | 
| 716 |     Removes the configuration property identified by \a key from the point at \a index | 
| 717 |     and restores the default look derived from the series' settings. | 
| 718 |  | 
| 719 |     Removes the configuration type, such as color or size, | 
| 720 |     specified by \a key from the point at \a index with configuration customizations, | 
| 721 |     allowing that configuration property to be rendered as the default | 
| 722 |     specified in the series' properties. | 
| 723 |  | 
| 724 |     \note It doesn't affect the configuration of other points. | 
| 725 |     \sa clearPointsConfiguration(), setPointConfiguration() | 
| 726 |     \since 6.2 | 
| 727 | */ | 
| 728 | void QXYSeries::clearPointConfiguration(const int index, const QXYSeries::PointConfiguration key) | 
| 729 | { | 
| 730 |     Q_D(QXYSeries); | 
| 731 |     if (d->m_pointsConfiguration.contains(key: index)) { | 
| 732 |         auto &conf = d->m_pointsConfiguration[index]; | 
| 733 |         if (conf.contains(key)) { | 
| 734 |             conf.remove(key); | 
| 735 |             d->m_pointsConfiguration[index] = conf; | 
| 736 |             emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 737 |         } | 
| 738 |     } | 
| 739 | } | 
| 740 |  | 
| 741 | /*! | 
| 742 |     Removes the configuration of all points in the series and restores | 
| 743 |     the default look derived from the series' settings. | 
| 744 |  | 
| 745 |     \sa setPointConfiguration() | 
| 746 |     \since 6.2 | 
| 747 | */ | 
| 748 | void QXYSeries::clearPointsConfiguration() | 
| 749 | { | 
| 750 |     Q_D(QXYSeries); | 
| 751 |     d->m_pointsConfiguration.clear(); | 
| 752 |     emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 753 | } | 
| 754 |  | 
| 755 | /*! | 
| 756 |     Removes the configuration property identified by \a key from all | 
| 757 |     points and restores the default look derived from the series' settings. | 
| 758 |  | 
| 759 |     Removes the configuration type, such as color or size, | 
| 760 |     specified by \a key from all points with configuration customizations, | 
| 761 |     allowing that configuration property to be rendered as the default | 
| 762 |     specified in the series properties. | 
| 763 |  | 
| 764 |     \sa clearPointsConfiguration(), setPointConfiguration() | 
| 765 |     \since 6.2 | 
| 766 | */ | 
| 767 | void QXYSeries::clearPointsConfiguration(const QXYSeries::PointConfiguration key) | 
| 768 | { | 
| 769 |     Q_D(QXYSeries); | 
| 770 |     bool needsUpdate = false; | 
| 771 |     for (const int &index : d->m_pointsConfiguration.keys()) { | 
| 772 |         auto &conf = d->m_pointsConfiguration[index]; | 
| 773 |         if (conf.contains(key)) { | 
| 774 |             conf.remove(key); | 
| 775 |             d->m_pointsConfiguration[index] = conf; | 
| 776 |             needsUpdate = true; | 
| 777 |         } | 
| 778 |     } | 
| 779 |  | 
| 780 |     if (needsUpdate) | 
| 781 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 782 | } | 
| 783 |  | 
| 784 | /*! | 
| 785 |     Enables customizing the appearance of a point located at \a index with | 
| 786 |     desired \a configuration. | 
| 787 |  | 
| 788 |     With points configuration you can change various aspects of every point's appearance. | 
| 789 |  | 
| 790 |     A point's configuration is represented as a hash map with QXYSeries::pointConfiguration | 
| 791 |     keys and QVariant values. For example: | 
| 792 |     \code | 
| 793 |     QLineSeries *series = new QLineSeries(); | 
| 794 |     series->setName("Customized series"); | 
| 795 |     series->setPointsVisible(true); | 
| 796 |  | 
| 797 |     *series << QPointF(0, 6) << QPointF(2, 4) << QPointF(3, 6) << QPointF(7, 4) << QPointF(10, 5) | 
| 798 |             << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18, 3) | 
| 799 |             << QPointF(20, 2); | 
| 800 |  | 
| 801 |     QChart *chart = new QChart(); | 
| 802 |     chart->addSeries(series); | 
| 803 |     chart->createDefaultAxes(); | 
| 804 |  | 
| 805 |     QHash<QXYSeries::PointConfiguration, QVariant> conf; | 
| 806 |     conf[QXYSeries::PointConfiguration::Color] = QColor(Qt::red); | 
| 807 |     conf[QXYSeries::PointConfiguration::Size] = 8; | 
| 808 |     conf[QXYSeries::PointConfiguration::LabelVisibility] = true; | 
| 809 |  | 
| 810 |     series->setPointConfiguration(4, conf); | 
| 811 |  | 
| 812 |     conf.remove(QXYSeries::PointConfiguration::Color); | 
| 813 |     conf[QXYSeries::PointConfiguration::LabelFormat] = "This Point"; | 
| 814 |     series->setPointConfiguration(6, conf); | 
| 815 |     \endcode | 
| 816 |  | 
| 817 |     In this example, you can see a default QLineSeries with 10 points and with changed configuration | 
| 818 |     of two points. Both changed points are visibly bigger than the others with a look derived from | 
| 819 |     the series configuration. | 
| 820 |     By default, points don't have labels, but the point at index 4 has a label thanks to the | 
| 821 |     QXYSeries::PointConfiguration::LabelVisibility and QXYSeries::PointConfiguration::LabelFormat | 
| 822 |     configuration values. | 
| 823 |     The point at index 6 has a custom label \e {This Point} thanks to the | 
| 824 |     QXYSeries::PointConfiguration::LabelFormat configuration value. | 
| 825 |     Below is an example of a chart created in this way: | 
| 826 |     \image xyseries_point_configuration.png | 
| 827 |  | 
| 828 |     \sa pointsConfiguration(), clearPointsConfiguration() | 
| 829 |     \since 6.2 | 
| 830 | */ | 
| 831 | void QXYSeries::setPointConfiguration( | 
| 832 |         const int index, const QHash<QXYSeries::PointConfiguration, QVariant> &configuration) | 
| 833 | { | 
| 834 |     Q_D(QXYSeries); | 
| 835 |     if (d->m_pointsConfiguration[index] != configuration) { | 
| 836 |         d->m_pointsConfiguration[index] = configuration; | 
| 837 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 838 |     } | 
| 839 | } | 
| 840 |  | 
| 841 | /*! | 
| 842 |     Enables customizing a particular aspect of a point's configuration. | 
| 843 |  | 
| 844 |     \note Points configuration concept provides a flexible way to configure various aspects | 
| 845 |     of a point's appearance. Thus, values need to have an elastic type such as QVariant. | 
| 846 |     See QXYSeries::PointConfiguration to see what \a value should be passed | 
| 847 |     for certain \a key. | 
| 848 |     \sa pointsConfiguration() | 
| 849 |     \since 6.2 | 
| 850 | */ | 
| 851 | void QXYSeries::setPointConfiguration(const int index, const QXYSeries::PointConfiguration key, | 
| 852 |                                       const QVariant &value) | 
| 853 | { | 
| 854 |     Q_D(QXYSeries); | 
| 855 |     if (d->setPointConfiguration(index, key, value)) | 
| 856 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 857 | } | 
| 858 |  | 
| 859 | /*! | 
| 860 |     Enables customizing the configuration of multiple points as specified | 
| 861 |     by \a pointsConfiguration. | 
| 862 |  | 
| 863 |     \sa pointsConfiguration() | 
| 864 |     \since 6.2 | 
| 865 | */ | 
| 866 | void QXYSeries::setPointsConfiguration( | 
| 867 |         const QHash<int, QHash<QXYSeries::PointConfiguration, QVariant>> &pointsConfiguration) | 
| 868 | { | 
| 869 |     Q_D(QXYSeries); | 
| 870 |     if (d->m_pointsConfiguration != pointsConfiguration) { | 
| 871 |         d->m_pointsConfiguration = pointsConfiguration; | 
| 872 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 873 |     } | 
| 874 | } | 
| 875 |  | 
| 876 | /*! | 
| 877 |     Returns a map representing the configuration of a point at \a index. | 
| 878 |  | 
| 879 |     With points configuration you can change various aspects of each point's look. | 
| 880 |  | 
| 881 |     \sa setPointConfiguration() | 
| 882 |     \since 6.2 | 
| 883 | */ | 
| 884 | QHash<QXYSeries::PointConfiguration, QVariant> QXYSeries::pointConfiguration(const int index) const | 
| 885 | { | 
| 886 |     Q_D(const QXYSeries); | 
| 887 |     return d->m_pointsConfiguration[index]; | 
| 888 | } | 
| 889 |  | 
| 890 | /*! | 
| 891 |     Returns a map with points' indexes as keys and points' configuration as values. | 
| 892 |  | 
| 893 |     \sa setPointConfiguration(), pointConfiguration() | 
| 894 |     \since 6.2 | 
| 895 | */ | 
| 896 | QHash<int, QHash<QXYSeries::PointConfiguration, QVariant>> QXYSeries::pointsConfiguration() const | 
| 897 | { | 
| 898 |     Q_D(const QXYSeries); | 
| 899 |     return d->m_pointsConfiguration; | 
| 900 | } | 
| 901 |  | 
| 902 | /*! | 
| 903 |     Sets the points' sizes according to a passed list of values. Values from | 
| 904 |     \a sourceData are sorted and mapped to a point size which is between \a minSize | 
| 905 |     and \a maxSize. | 
| 906 |  | 
| 907 |     \note If \a sourceData length is smaller than number of points in the series, then | 
| 908 |     size of the points at the end of the series will stay the same. | 
| 909 |     \sa setPointConfiguration(), pointConfiguration() | 
| 910 |     \since 6.2 | 
| 911 | */ | 
| 912 | void QXYSeries::sizeBy(const QList<qreal> &sourceData, const qreal minSize, const qreal maxSize) | 
| 913 | { | 
| 914 |     Q_D(QXYSeries); | 
| 915 |  | 
| 916 |     Q_ASSERT(minSize <= maxSize); | 
| 917 |     Q_ASSERT(minSize >= 0); | 
| 918 |  | 
| 919 |     qreal min = std::numeric_limits<qreal>::max(); | 
| 920 |     qreal max = -std::numeric_limits<qreal>::max(); | 
| 921 |     for (const auto &p : sourceData) { | 
| 922 |         min = qMin(a: min, b: p); | 
| 923 |         max = qMax(a: max, b: p); | 
| 924 |     } | 
| 925 |  | 
| 926 |     const qreal range = max - min; | 
| 927 |     const qreal sizeRange = maxSize - minSize; | 
| 928 |     bool changed = false; | 
| 929 |  | 
| 930 |     for (int i = 0; i < sourceData.size() && i < d->m_points.size(); ++i) { | 
| 931 |         qreal pointSize = minSize; | 
| 932 |         if (range != 0) { | 
| 933 |             const qreal startValue = sourceData.at(i) - min; | 
| 934 |             const qreal percentage = startValue / range; | 
| 935 |             pointSize = minSize + (percentage * sizeRange); | 
| 936 |         } | 
| 937 |         if (d->setPointConfiguration(index: i, key: QXYSeries::PointConfiguration::Size, value: pointSize)) | 
| 938 |             changed = true; | 
| 939 |     } | 
| 940 |  | 
| 941 |     if (changed) | 
| 942 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 943 | } | 
| 944 |  | 
| 945 | /*! | 
| 946 |     Sets the points' color according to a passed list of values. Values from | 
| 947 |     \a sourceData are sorted and mapped to the \a gradient. | 
| 948 |  | 
| 949 |     If the series has a QColorAxis attached, then a gradient from the axis | 
| 950 |     is going to be used. | 
| 951 |  | 
| 952 |     \sa setPointConfiguration(), pointConfiguration(), QColorAxis | 
| 953 |     \since 6.2 | 
| 954 | */ | 
| 955 | void QXYSeries::colorBy(const QList<qreal> &sourceData, const QLinearGradient &gradient) | 
| 956 | { | 
| 957 |     Q_D(QXYSeries); | 
| 958 |  | 
| 959 |     d->m_colorByData = sourceData; | 
| 960 |     if (d->m_colorByData.isEmpty()) | 
| 961 |         return; | 
| 962 |  | 
| 963 |     const qreal imgSize = 100.0; | 
| 964 |  | 
| 965 |     qreal min = std::numeric_limits<qreal>::max(); | 
| 966 |     qreal max = std::numeric_limits<qreal>::min(); | 
| 967 |     for (const auto &p : sourceData) { | 
| 968 |         min = qMin(a: min, b: p); | 
| 969 |         max = qMax(a: max, b: p); | 
| 970 |     } | 
| 971 |  | 
| 972 |     qreal range = max - min; | 
| 973 |  | 
| 974 |     QLinearGradient usedGradient = gradient; | 
| 975 |  | 
| 976 |     // Gradient will be taked from the first attached color axis. | 
| 977 |     // If there are more color axis, they will have just changed range. | 
| 978 |     bool axisFound = false; | 
| 979 |     const auto axes = attachedAxes(); | 
| 980 |     for (const auto &axis : axes) { | 
| 981 |         if (axis->type() == QAbstractAxis::AxisTypeColor) { | 
| 982 |             QColorAxis *colorAxis = static_cast<QColorAxis *>(axis); | 
| 983 |             if (!axisFound) { | 
| 984 |                 usedGradient = QLinearGradient(QPointF(0,0), QPointF(0, imgSize)); | 
| 985 |                 const auto stops = colorAxis->gradient().stops(); | 
| 986 |                 for (const auto &stop : stops) | 
| 987 |                     usedGradient.setColorAt(pos: stop.first, color: stop.second); | 
| 988 |  | 
| 989 |                 if (!colorAxis->autoRange()) { | 
| 990 |                     min = colorAxis->min(); | 
| 991 |                     max = colorAxis->max(); | 
| 992 |                     range = max - min; | 
| 993 |                 } | 
| 994 |  | 
| 995 |                 axisFound = true; | 
| 996 |             } | 
| 997 |  | 
| 998 |             if (colorAxis->autoRange()) | 
| 999 |                 colorAxis->setRange(min, max); | 
| 1000 |         } | 
| 1001 |     } | 
| 1002 |  | 
| 1003 |     QImage image(imgSize, imgSize, QImage::Format_ARGB32); | 
| 1004 |     QPainter painter(&image); | 
| 1005 |     painter.fillRect(image.rect(), usedGradient); | 
| 1006 |  | 
| 1007 |     // To ensure that negative values will be well handled, distance from min to 0 | 
| 1008 |     // will be added to min and every single value. This will move entire values | 
| 1009 |     // list to positive only values. | 
| 1010 |     const qreal diff = min < 0 ? qAbs(t: min) : 0; | 
| 1011 |     min += diff; | 
| 1012 |  | 
| 1013 |     bool changed = false; | 
| 1014 |  | 
| 1015 |     for (int i = 0; i < sourceData.size() && i < d->m_points.size(); ++i) { | 
| 1016 |         const qreal startValue = qMax(a: 0.0, b: sourceData.at(i) + diff - min); | 
| 1017 |         const qreal percentage = startValue / range; | 
| 1018 |         QColor color = image.pixelColor(x: 0, y: qMin(a: percentage * imgSize, b: imgSize - 1)); | 
| 1019 |         if (d->setPointConfiguration(index: i, key: QXYSeries::PointConfiguration::Color, value: color)) | 
| 1020 |             changed = true; | 
| 1021 |     } | 
| 1022 |  | 
| 1023 |     if (changed) | 
| 1024 |         emit pointsConfigurationChanged(configuration: d->m_pointsConfiguration); | 
| 1025 | } | 
| 1026 |  | 
| 1027 | /*! | 
| 1028 |    Returns true if point at given \a index is among selected points and false otherwise. | 
| 1029 |    \note Selected points are drawn using the selected color if it was specified. | 
| 1030 |    \sa selectedPoints(), setPointSelected(), setSelectedColor() | 
| 1031 |    \since 6.2 | 
| 1032 |  */ | 
| 1033 | bool QXYSeries::isPointSelected(int index) | 
| 1034 | { | 
| 1035 |     Q_D(QXYSeries); | 
| 1036 |     return d->isPointSelected(index); | 
| 1037 | } | 
| 1038 |  | 
| 1039 | /*! | 
| 1040 |   Marks point at \a index as selected. | 
| 1041 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1042 |   \sa setPointSelected() | 
| 1043 |   \since 6.2 | 
| 1044 |  */ | 
| 1045 | void QXYSeries::selectPoint(int index) | 
| 1046 | { | 
| 1047 |     setPointSelected(index, selected: true); | 
| 1048 | } | 
| 1049 |  | 
| 1050 | /*! | 
| 1051 |   Deselects point at given \a index. | 
| 1052 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1053 |   \sa setPointSelected() | 
| 1054 |   \since 6.2 | 
| 1055 |  */ | 
| 1056 | void QXYSeries::deselectPoint(int index) | 
| 1057 | { | 
| 1058 |     setPointSelected(index, selected: false); | 
| 1059 | } | 
| 1060 |  | 
| 1061 | /*! | 
| 1062 |   Marks point at given \a index as either selected or deselected as specified by \a selected. | 
| 1063 |   \note Selected points are drawn using the selected color if it was specified. Emits QXYSeries::selectedPointsChanged | 
| 1064 |   \sa setPointSelected(), setSelectedColor() | 
| 1065 |   \since 6.2 | 
| 1066 |  */ | 
| 1067 | void QXYSeries::setPointSelected(int index, bool selected) | 
| 1068 | { | 
| 1069 |     Q_D(QXYSeries); | 
| 1070 |  | 
| 1071 |     bool callSignal = false; | 
| 1072 |     d->setPointSelected(index, selected, callSignal); | 
| 1073 |  | 
| 1074 |     if (callSignal) | 
| 1075 |         emit selectedPointsChanged(); | 
| 1076 | } | 
| 1077 |  | 
| 1078 | /*! | 
| 1079 |   Marks all points in the series as selected, | 
| 1080 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1081 |   \sa setPointSelected() | 
| 1082 |   \since 6.2 | 
| 1083 |  */ | 
| 1084 | void QXYSeries::selectAllPoints() | 
| 1085 | { | 
| 1086 |     Q_D(QXYSeries); | 
| 1087 |  | 
| 1088 |     bool callSignal = false; | 
| 1089 |     for (int i = 0; i < d->m_points.size(); ++i) | 
| 1090 |         d->setPointSelected(index: i, selected: true, callSignal); | 
| 1091 |  | 
| 1092 |     if (callSignal) | 
| 1093 |         emit selectedPointsChanged(); | 
| 1094 | } | 
| 1095 |  | 
| 1096 | /*! | 
| 1097 |   Deselects all points in the series. | 
| 1098 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1099 |   \sa setPointSelected() | 
| 1100 |   \since 6.2 | 
| 1101 |  */ | 
| 1102 | void QXYSeries::deselectAllPoints() | 
| 1103 | { | 
| 1104 |     Q_D(QXYSeries); | 
| 1105 |  | 
| 1106 |     bool callSignal = false; | 
| 1107 |     for (int i = 0; i < d->m_points.size(); ++i) | 
| 1108 |         d->setPointSelected(index: i, selected: false, callSignal); | 
| 1109 |  | 
| 1110 |     if (callSignal) | 
| 1111 |         emit selectedPointsChanged(); | 
| 1112 | } | 
| 1113 |  | 
| 1114 | /*! | 
| 1115 |   Marks multiple points passed in a \a indexes list as selected. | 
| 1116 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1117 |   \sa setPointSelected() | 
| 1118 |   \since 6.2 | 
| 1119 |  */ | 
| 1120 | void QXYSeries::selectPoints(const QList<int> &indexes) | 
| 1121 | { | 
| 1122 |     Q_D(QXYSeries); | 
| 1123 |  | 
| 1124 |     bool callSignal = false; | 
| 1125 |     for (const int &index : indexes) | 
| 1126 |         d->setPointSelected(index, selected: true, callSignal); | 
| 1127 |  | 
| 1128 |     if (callSignal) | 
| 1129 |         emit selectedPointsChanged(); | 
| 1130 | } | 
| 1131 |  | 
| 1132 | /*! | 
| 1133 |   Marks multiple points passed in a \a indexes list as deselected. | 
| 1134 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1135 |   \sa setPointSelected() | 
| 1136 |   \since 6.2 | 
| 1137 |  */ | 
| 1138 | void QXYSeries::deselectPoints(const QList<int> &indexes) | 
| 1139 | { | 
| 1140 |     Q_D(QXYSeries); | 
| 1141 |  | 
| 1142 |     bool callSignal = false; | 
| 1143 |     for (const int &index : indexes) | 
| 1144 |         d->setPointSelected(index, selected: false, callSignal); | 
| 1145 |  | 
| 1146 |     if (callSignal) | 
| 1147 |         emit selectedPointsChanged(); | 
| 1148 | } | 
| 1149 |  | 
| 1150 | /*! | 
| 1151 |   Changes selection state of points at given \a indexes to the opposite one. Makes | 
| 1152 |   \note Emits QXYSeries::selectedPointsChanged | 
| 1153 |   \sa setPointSelected() | 
| 1154 |   \since 6.2 | 
| 1155 |  */ | 
| 1156 | void QXYSeries::toggleSelection(const QList<int> &indexes) | 
| 1157 | { | 
| 1158 |     Q_D(QXYSeries); | 
| 1159 |  | 
| 1160 |     bool callSignal = false; | 
| 1161 |     for (const int &index : indexes) | 
| 1162 |         d->setPointSelected(index, selected: !isPointSelected(index), callSignal); | 
| 1163 |  | 
| 1164 |     if (callSignal) | 
| 1165 |         emit selectedPointsChanged(); | 
| 1166 | } | 
| 1167 |  | 
| 1168 | /*! | 
| 1169 |   Returns a list of points indexes marked as selected. | 
| 1170 |   Selected points are visible regardless of points visibility. | 
| 1171 |   \sa setPointSelected(), pointsVisible() | 
| 1172 |   \since 6.2 | 
| 1173 |  */ | 
| 1174 | QList<int> QXYSeries::selectedPoints() const | 
| 1175 | { | 
| 1176 |     Q_D(const QXYSeries); | 
| 1177 |     return QList<int>(d->m_selectedPoints.begin(), d->m_selectedPoints.end()); | 
| 1178 | } | 
| 1179 |  | 
| 1180 | /*! | 
| 1181 |   Removes the point that has the coordinates \a x and \a y from the series. | 
| 1182 |   \sa pointRemoved() | 
| 1183 | */ | 
| 1184 | void QXYSeries::remove(qreal x, qreal y) | 
| 1185 | { | 
| 1186 |     remove(point: QPointF(x, y)); | 
| 1187 | } | 
| 1188 |  | 
| 1189 | /*! | 
| 1190 |   Removes the data point \a point from the series. | 
| 1191 |   \sa pointRemoved() | 
| 1192 | */ | 
| 1193 | void QXYSeries::remove(const QPointF &point) | 
| 1194 | { | 
| 1195 |     Q_D(QXYSeries); | 
| 1196 |     int index = d->m_points.indexOf(t: point); | 
| 1197 |     if (index == -1) | 
| 1198 |         return; | 
| 1199 |     remove(index); | 
| 1200 | } | 
| 1201 |  | 
| 1202 | /*! | 
| 1203 |   Removes the point at the position specified by \a index from the series. | 
| 1204 |   \sa pointRemoved() | 
| 1205 | */ | 
| 1206 | void QXYSeries::remove(int index) | 
| 1207 | { | 
| 1208 |     Q_D(QXYSeries); | 
| 1209 |     d->m_points.remove(i: index); | 
| 1210 |  | 
| 1211 |     bool callSignal = false; | 
| 1212 |     d->setPointSelected(index, selected: false, callSignal); | 
| 1213 |  | 
| 1214 |     emit pointRemoved(index); | 
| 1215 |     if (callSignal) | 
| 1216 |         emit selectedPointsChanged(); | 
| 1217 | } | 
| 1218 |  | 
| 1219 | /*! | 
| 1220 |   Removes the number of points specified by \a count from the series starting at | 
| 1221 |   the position specified by \a index. | 
| 1222 |   \sa pointsRemoved() | 
| 1223 | */ | 
| 1224 | void QXYSeries::removePoints(int index, int count) | 
| 1225 | { | 
| 1226 |     // This function doesn't overload remove as there is chance for it to get mixed up with | 
| 1227 |     // remove(qreal, qreal) overload in some implicit casting cases. | 
| 1228 |     Q_D(QXYSeries); | 
| 1229 |     if (count > 0) { | 
| 1230 |         d->m_points.remove(i: index, n: count); | 
| 1231 |  | 
| 1232 |         bool callSignal = false; | 
| 1233 |         if (!d->m_selectedPoints.empty()) { | 
| 1234 |             QSet<int> selectedAfterRemoving; | 
| 1235 |  | 
| 1236 |             for (const int &selectedPointIndex : std::as_const(t&: d->m_selectedPoints)) { | 
| 1237 |                 if (selectedPointIndex < index) { | 
| 1238 |                     selectedAfterRemoving << selectedPointIndex; | 
| 1239 |                 } else if (selectedPointIndex >= index + count) { | 
| 1240 |                     selectedAfterRemoving << selectedPointIndex - count; | 
| 1241 |                     callSignal = true; | 
| 1242 |                 } else { | 
| 1243 |                     callSignal = true; | 
| 1244 |                 } | 
| 1245 |             } | 
| 1246 |  | 
| 1247 |             d->m_selectedPoints = selectedAfterRemoving; | 
| 1248 |         } | 
| 1249 |  | 
| 1250 |         emit pointsRemoved(index, count); | 
| 1251 |         if (callSignal) | 
| 1252 |             emit selectedPointsChanged(); | 
| 1253 |     } | 
| 1254 | } | 
| 1255 |  | 
| 1256 | /*! | 
| 1257 |   Inserts the data point \a point in the series at the position specified by | 
| 1258 |   \a index. | 
| 1259 |   \sa pointAdded() | 
| 1260 | */ | 
| 1261 | void QXYSeries::insert(int index, const QPointF &point) | 
| 1262 | { | 
| 1263 |     Q_D(QXYSeries); | 
| 1264 |     if (isValidValue(point)) { | 
| 1265 |         index = qMax(a: 0, b: qMin(a: index, b: d->m_points.size())); | 
| 1266 |  | 
| 1267 |         d->m_points.insert(i: index, t: point); | 
| 1268 |  | 
| 1269 |         bool callSignal = false; | 
| 1270 |         if (!d->m_selectedPoints.isEmpty()) { | 
| 1271 |             // if point was inserted we need to move already selected points by 1 | 
| 1272 |             QSet<int> selectedAfterInsert; | 
| 1273 |             for (const auto &value : std::as_const(t&: d->m_selectedPoints)) { | 
| 1274 |                 if (value >= index) { | 
| 1275 |                     selectedAfterInsert << value + 1; | 
| 1276 |                     callSignal = true; | 
| 1277 |                 } else { | 
| 1278 |                     selectedAfterInsert << value; | 
| 1279 |                 } | 
| 1280 |             } | 
| 1281 |             d->m_selectedPoints = selectedAfterInsert; | 
| 1282 |         } | 
| 1283 |  | 
| 1284 |         emit pointAdded(index); | 
| 1285 |         if (callSignal) | 
| 1286 |             emit selectedPointsChanged(); | 
| 1287 |     } | 
| 1288 | } | 
| 1289 |  | 
| 1290 | /*! | 
| 1291 |   Removes all points from the series. | 
| 1292 |   \sa pointsRemoved() | 
| 1293 | */ | 
| 1294 | void QXYSeries::clear() | 
| 1295 | { | 
| 1296 |     Q_D(QXYSeries); | 
| 1297 |     removePoints(index: 0, count: d->m_points.size()); | 
| 1298 | } | 
| 1299 |  | 
| 1300 | /*! | 
| 1301 |     Returns the points in the series. | 
| 1302 | */ | 
| 1303 | QList<QPointF> QXYSeries::points() const | 
| 1304 | { | 
| 1305 |     Q_D(const QXYSeries); | 
| 1306 |     return d->m_points; | 
| 1307 | } | 
| 1308 |  | 
| 1309 | #if QT_DEPRECATED_SINCE(6, 0) | 
| 1310 | /*! | 
| 1311 |     \deprecated | 
| 1312 |  | 
| 1313 |     Use points() instead. | 
| 1314 |     Returns the points in the series. | 
| 1315 |  | 
| 1316 | */ | 
| 1317 | QList<QPointF> QXYSeries::pointsVector() const | 
| 1318 | { | 
| 1319 |     Q_D(const QXYSeries); | 
| 1320 |     return d->m_points; | 
| 1321 | } | 
| 1322 | #endif | 
| 1323 |  | 
| 1324 | /*! | 
| 1325 |     Returns the data point at the position specified by \a index in the internal | 
| 1326 |     series of points. | 
| 1327 | */ | 
| 1328 | const QPointF &QXYSeries::at(int index) const | 
| 1329 | { | 
| 1330 |     Q_D(const QXYSeries); | 
| 1331 |     return d->m_points.at(i: index); | 
| 1332 | } | 
| 1333 |  | 
| 1334 | /*! | 
| 1335 |     Returns the number of data points in a series. | 
| 1336 | */ | 
| 1337 | int QXYSeries::count() const | 
| 1338 | { | 
| 1339 |     Q_D(const QXYSeries); | 
| 1340 |     return d->m_points.size(); | 
| 1341 | } | 
| 1342 |  | 
| 1343 |  | 
| 1344 | /*! | 
| 1345 |     Sets the pen used for drawing points on the chart to \a pen. If the pen is | 
| 1346 |     not defined, the pen from the chart theme is used. | 
| 1347 |     \sa QChart::setTheme() | 
| 1348 | */ | 
| 1349 | void QXYSeries::setPen(const QPen &pen) | 
| 1350 | { | 
| 1351 |     Q_D(QXYSeries); | 
| 1352 |     if (d->m_pen != pen) { | 
| 1353 |         bool emitColorChanged = d->m_pen.color() != pen.color(); | 
| 1354 |         d->m_pen = pen; | 
| 1355 |         emit d->seriesUpdated(); | 
| 1356 |         if (emitColorChanged) | 
| 1357 |             emit colorChanged(color: pen.color()); | 
| 1358 |         emit penChanged(pen); | 
| 1359 |     } | 
| 1360 | } | 
| 1361 |  | 
| 1362 | QPen QXYSeries::pen() const | 
| 1363 | { | 
| 1364 |     Q_D(const QXYSeries); | 
| 1365 |     if (d->m_pen == QChartPrivate::defaultPen()) | 
| 1366 |         return QPen(); | 
| 1367 |     else | 
| 1368 |         return d->m_pen; | 
| 1369 | } | 
| 1370 |  | 
| 1371 | /*! | 
| 1372 |     Sets the brush used for drawing points on the chart to \a brush. If the | 
| 1373 |     brush is not defined, the brush from the chart theme setting is used. | 
| 1374 |     \sa QChart::setTheme() | 
| 1375 | */ | 
| 1376 | void QXYSeries::setBrush(const QBrush &brush) | 
| 1377 | { | 
| 1378 |     Q_D(QXYSeries); | 
| 1379 |     if (d->m_brush != brush) { | 
| 1380 |         d->m_brush = brush; | 
| 1381 |         emit d->seriesUpdated(); | 
| 1382 |     } | 
| 1383 | } | 
| 1384 |  | 
| 1385 | QBrush QXYSeries::brush() const | 
| 1386 | { | 
| 1387 |     Q_D(const QXYSeries); | 
| 1388 |     if (d->m_brush == QChartPrivate::defaultBrush()) | 
| 1389 |         return QBrush(); | 
| 1390 |     else | 
| 1391 |         return d->m_brush; | 
| 1392 | } | 
| 1393 |  | 
| 1394 | void QXYSeries::setColor(const QColor &color) | 
| 1395 | { | 
| 1396 |     QPen p = pen(); | 
| 1397 |     if (p.color() != color) { | 
| 1398 |         p.setColor(color); | 
| 1399 |         setPen(p); | 
| 1400 |     } | 
| 1401 | } | 
| 1402 |  | 
| 1403 | QColor QXYSeries::color() const | 
| 1404 | { | 
| 1405 |     return pen().color(); | 
| 1406 | } | 
| 1407 |  | 
| 1408 | void QXYSeries::setSelectedColor(const QColor &color) | 
| 1409 | { | 
| 1410 |     Q_D(QXYSeries); | 
| 1411 |     if (selectedColor() != color) { | 
| 1412 |         d->m_selectedColor = color; | 
| 1413 |         emit selectedColorChanged(color); | 
| 1414 |     } | 
| 1415 | } | 
| 1416 |  | 
| 1417 | QColor QXYSeries::selectedColor() const | 
| 1418 | { | 
| 1419 |     Q_D(const QXYSeries); | 
| 1420 |     return d->m_selectedColor; | 
| 1421 | } | 
| 1422 |  | 
| 1423 | void QXYSeries::setPointsVisible(bool visible) | 
| 1424 | { | 
| 1425 |     Q_D(QXYSeries); | 
| 1426 |     if (d->m_pointsVisible != visible) { | 
| 1427 |         d->m_pointsVisible = visible; | 
| 1428 |         emit d->seriesUpdated(); | 
| 1429 |     } | 
| 1430 | } | 
| 1431 |  | 
| 1432 | bool QXYSeries::pointsVisible() const | 
| 1433 | { | 
| 1434 |     Q_D(const QXYSeries); | 
| 1435 |     return d->m_pointsVisible; | 
| 1436 | } | 
| 1437 |  | 
| 1438 | void QXYSeries::setPointLabelsFormat(const QString &format) | 
| 1439 | { | 
| 1440 |     Q_D(QXYSeries); | 
| 1441 |     if (d->m_pointLabelsFormat != format) { | 
| 1442 |         d->m_pointLabelsFormat = format; | 
| 1443 |         emit pointLabelsFormatChanged(format); | 
| 1444 |     } | 
| 1445 | } | 
| 1446 |  | 
| 1447 | QString QXYSeries::pointLabelsFormat() const | 
| 1448 | { | 
| 1449 |     Q_D(const QXYSeries); | 
| 1450 |     return d->m_pointLabelsFormat; | 
| 1451 | } | 
| 1452 |  | 
| 1453 | void QXYSeries::setPointLabelsVisible(bool visible) | 
| 1454 | { | 
| 1455 |     Q_D(QXYSeries); | 
| 1456 |     if (d->m_pointLabelsVisible != visible) { | 
| 1457 |         d->m_pointLabelsVisible = visible; | 
| 1458 |         emit pointLabelsVisibilityChanged(visible); | 
| 1459 |     } | 
| 1460 | } | 
| 1461 |  | 
| 1462 | bool QXYSeries::pointLabelsVisible() const | 
| 1463 | { | 
| 1464 |     Q_D(const QXYSeries); | 
| 1465 |     return d->m_pointLabelsVisible; | 
| 1466 | } | 
| 1467 |  | 
| 1468 | void QXYSeries::setPointLabelsFont(const QFont &font) | 
| 1469 | { | 
| 1470 |     Q_D(QXYSeries); | 
| 1471 |     if (d->m_pointLabelsFont != font) { | 
| 1472 |         d->m_pointLabelsFont = font; | 
| 1473 |         emit pointLabelsFontChanged(font); | 
| 1474 |     } | 
| 1475 | } | 
| 1476 |  | 
| 1477 | QFont QXYSeries::pointLabelsFont() const | 
| 1478 | { | 
| 1479 |     Q_D(const QXYSeries); | 
| 1480 |     return d->m_pointLabelsFont; | 
| 1481 | } | 
| 1482 |  | 
| 1483 | void QXYSeries::setPointLabelsColor(const QColor &color) | 
| 1484 | { | 
| 1485 |     Q_D(QXYSeries); | 
| 1486 |     if (d->m_pointLabelsColor != color) { | 
| 1487 |         d->m_pointLabelsColor = color; | 
| 1488 |         emit pointLabelsColorChanged(color); | 
| 1489 |     } | 
| 1490 | } | 
| 1491 |  | 
| 1492 | QColor QXYSeries::pointLabelsColor() const | 
| 1493 | { | 
| 1494 |     Q_D(const QXYSeries); | 
| 1495 |     if (d->m_pointLabelsColor == QChartPrivate::defaultPen().color()) | 
| 1496 |         return QPen().color(); | 
| 1497 |     else | 
| 1498 |         return d->m_pointLabelsColor; | 
| 1499 | } | 
| 1500 |  | 
| 1501 | void QXYSeries::setPointLabelsClipping(bool enabled) | 
| 1502 | { | 
| 1503 |     Q_D(QXYSeries); | 
| 1504 |     if (d->m_pointLabelsClipping != enabled) { | 
| 1505 |         d->m_pointLabelsClipping = enabled; | 
| 1506 |         emit pointLabelsClippingChanged(clipping: enabled); | 
| 1507 |     } | 
| 1508 | } | 
| 1509 |  | 
| 1510 | bool QXYSeries::pointLabelsClipping() const | 
| 1511 | { | 
| 1512 |     Q_D(const QXYSeries); | 
| 1513 |     return d->m_pointLabelsClipping; | 
| 1514 | } | 
| 1515 |  | 
| 1516 | /*! | 
| 1517 |     Sets the image used for drawing markers on each point of the series as | 
| 1518 |     the value of \a lightMarker. | 
| 1519 |  | 
| 1520 |     The default value is a default-QImage() (QImage::isNull() == true), meaning no light marker | 
| 1521 |     will be painted. | 
| 1522 |     You can reset back to default (disabled) by calling this function with a null QImage (QImage()). | 
| 1523 |  | 
| 1524 |     The light markers visualize the data points of this series and as such are an alternative | 
| 1525 |     to \c setPointsVisible(true). | 
| 1526 |     If a light marker is set with this method, visible points as set with \c setPointsVisible(true) | 
| 1527 |     are not displayed. | 
| 1528 |  | 
| 1529 |     Unlike the elements of \l {QScatterSeries}{QScatterSeries} the light markers | 
| 1530 |     are not represented by QGraphicsItem, but are just painted (no objects created). | 
| 1531 |     However, the mouse-event-signals of QXYSeries behave the same way, | 
| 1532 |     meaning that you'll get the exact domain value of the point if you click/press/hover | 
| 1533 |     the light marker. You'll still get the in between domain value if you click on the line. | 
| 1534 |     The light markers are above the line in terms of painting as well as events. | 
| 1535 |  | 
| 1536 |     \sa QXYSeries::lightMarker() | 
| 1537 |     \since 6.2 | 
| 1538 | */ | 
| 1539 | void QXYSeries::setLightMarker(const QImage &lightMarker) | 
| 1540 | { | 
| 1541 |     Q_D(QXYSeries); | 
| 1542 |     if (d->m_lightMarker == lightMarker) | 
| 1543 |         return; | 
| 1544 |  | 
| 1545 |     d->m_lightMarker = lightMarker; | 
| 1546 |     emit d->seriesUpdated(); | 
| 1547 |     emit lightMarkerChanged(lightMarker: d->m_lightMarker); | 
| 1548 | } | 
| 1549 |  | 
| 1550 | /*! | 
| 1551 |     Gets the image used for drawing markers on each point of the series. | 
| 1552 |  | 
| 1553 |     The default value is QImage(), meaning no light marker will be painted. | 
| 1554 |  | 
| 1555 |     The light markers visualize the data points of this series and as such are an alternative | 
| 1556 |     to setPointsVisible(true). | 
| 1557 |     Both features can be enabled independently from each other. | 
| 1558 |  | 
| 1559 |     Unlike the elements of \l {QScatterSeries}{QScatterSeries} the light markers | 
| 1560 |     are not represented by QGraphicsItem, but are just painted (no objects created). | 
| 1561 |     However, the mouse-event-signals of QXYSeries behave the same way, | 
| 1562 |     meaning that you'll get the exact domain value of the point if you click/press/hover | 
| 1563 |     the light marker. You'll still get the in between domain value if you click on the line. | 
| 1564 |     The light markers are above the line in terms of painting as well as events. | 
| 1565 |     \sa QXYSeries::setLightMarker() | 
| 1566 |     \since 6.2 | 
| 1567 | */ | 
| 1568 | const QImage &QXYSeries::lightMarker() const | 
| 1569 | { | 
| 1570 |     Q_D(const QXYSeries); | 
| 1571 |     return d->m_lightMarker; | 
| 1572 | } | 
| 1573 |  | 
| 1574 | /*! | 
| 1575 |     Sets the image used for drawing markers on selected series's points to \a selectedLightMarker. | 
| 1576 |  | 
| 1577 |     The default value is QImage(), meaning usual lightMarker() will be painted. | 
| 1578 |  | 
| 1579 |     This is an equivalent for \l{selectedColor} if you prefer light markers over | 
| 1580 |     normal points, but still want to distinguish selected points. | 
| 1581 |  | 
| 1582 |     \sa lightMarker(), selectedColor, setPointSelected() | 
| 1583 |     \since 6.2 | 
| 1584 | */ | 
| 1585 | void QXYSeries::setSelectedLightMarker(const QImage &selectedLightMarker) | 
| 1586 | { | 
| 1587 |     Q_D(QXYSeries); | 
| 1588 |     if (d->m_selectedLightMarker == selectedLightMarker) | 
| 1589 |         return; | 
| 1590 |  | 
| 1591 |     d->m_selectedLightMarker = selectedLightMarker; | 
| 1592 |     emit d->seriesUpdated(); | 
| 1593 |     emit selectedLightMarkerChanged(selectedLightMarker: d->m_selectedLightMarker); | 
| 1594 | } | 
| 1595 |  | 
| 1596 | /*! | 
| 1597 |     Returns the image used for drawing markers on selected series' points. | 
| 1598 |  | 
| 1599 |     The default value is QImage(), meaning usual lightMarker() will be painted. | 
| 1600 |  | 
| 1601 |     This is equivalent to \l{selectedColor} if you prefer light markers over | 
| 1602 |     normal points, but still want to distinguish selected points. | 
| 1603 |  | 
| 1604 |     \sa lightMarker(), selectedColor, setPointSelected() | 
| 1605 |     \since 6.2 | 
| 1606 | */ | 
| 1607 | const QImage &QXYSeries::selectedLightMarker() const | 
| 1608 | { | 
| 1609 |     Q_D(const QXYSeries); | 
| 1610 |     return d->m_selectedLightMarker; | 
| 1611 | } | 
| 1612 |  | 
| 1613 | /*! | 
| 1614 |     Sets the \a size of the marker used to render points in the series. | 
| 1615 |  | 
| 1616 |     The default size is 15.0. | 
| 1617 |     \sa QScatterSeries::markerSize | 
| 1618 |     \since 6.2 | 
| 1619 | */ | 
| 1620 | void QXYSeries::setMarkerSize(qreal size) | 
| 1621 | { | 
| 1622 |     Q_D(QXYSeries); | 
| 1623 |  | 
| 1624 |     if (!qFuzzyCompare(p1: d->m_markerSize, p2: size)) { | 
| 1625 |         d->m_markerSizeDefault = false; | 
| 1626 |         d->setMarkerSize(size); | 
| 1627 |         emit d->seriesUpdated(); | 
| 1628 |         emit markerSizeChanged(size); | 
| 1629 |     } | 
| 1630 | } | 
| 1631 |  | 
| 1632 | /*! | 
| 1633 |     Gets the size of the marker used to render points in the series. | 
| 1634 |  | 
| 1635 |     The default size depends on the specific QXYSeries type. | 
| 1636 |     QScatterSeries has a default of 15.0 | 
| 1637 |     QLineSeries has a default of the series pen size * 1.5 | 
| 1638 |     \sa QScatterSeries::markerSize | 
| 1639 |     \since 6.2 | 
| 1640 | */ | 
| 1641 | qreal QXYSeries::markerSize() const | 
| 1642 | { | 
| 1643 |     Q_D(const QXYSeries); | 
| 1644 |     return d->m_markerSize; | 
| 1645 | } | 
| 1646 |  | 
| 1647 | void QXYSeries::setBestFitLineVisible(bool visible) | 
| 1648 | { | 
| 1649 |     Q_D(QXYSeries); | 
| 1650 |     if (d->m_bestFitLineVisible != visible) { | 
| 1651 |         d->m_bestFitLineVisible = visible; | 
| 1652 |         emit bestFitLineVisibilityChanged(visible); | 
| 1653 |         emit d->seriesUpdated(); | 
| 1654 |     } | 
| 1655 | } | 
| 1656 |  | 
| 1657 | bool QXYSeries::bestFitLineVisible() const | 
| 1658 | { | 
| 1659 |     Q_D(const QXYSeries); | 
| 1660 |     return d->m_bestFitLineVisible; | 
| 1661 | } | 
| 1662 |  | 
| 1663 | /*! | 
| 1664 |     Returns a pair of numbers where the first number is a slope factor | 
| 1665 |     and the second number is intercept of a linear function for a best fit line. | 
| 1666 |  | 
| 1667 |     Those factors are calculated using Least Squares Method based | 
| 1668 |     on points passed to the series. | 
| 1669 |  | 
| 1670 |     Parameter \a ok is used to report a failure by setting its value to \c false | 
| 1671 |     and to report a success by setting its value to \c true. | 
| 1672 |  | 
| 1673 |     \sa QXYSeries::bestFitLineVisible() | 
| 1674 |     \since 6.2 | 
| 1675 | */ | 
| 1676 | QPair<qreal, qreal> QXYSeries::bestFitLineEquation(bool &ok) const | 
| 1677 | { | 
| 1678 |     Q_D(const QXYSeries); | 
| 1679 |     return d->bestFitLineEquation(ok); | 
| 1680 | } | 
| 1681 |  | 
| 1682 | void QXYSeries::setBestFitLinePen(const QPen &pen) | 
| 1683 | { | 
| 1684 |     Q_D(QXYSeries); | 
| 1685 |     if (d->m_bestFitLinePen != pen) { | 
| 1686 |         bool emitColorChanged = d->m_bestFitLinePen.color() != pen.color(); | 
| 1687 |         d->m_bestFitLinePen = pen; | 
| 1688 |         emit d->seriesUpdated(); | 
| 1689 |         if (emitColorChanged) | 
| 1690 |             bestFitLineColorChanged(color: pen.color()); | 
| 1691 |         emit bestFitLinePenChanged(pen); | 
| 1692 |     } | 
| 1693 | } | 
| 1694 |  | 
| 1695 | QPen QXYSeries::bestFitLinePen() const | 
| 1696 | { | 
| 1697 |     Q_D(const QXYSeries); | 
| 1698 |     if (d->m_bestFitLinePen == QChartPrivate::defaultPen()) | 
| 1699 |         return QPen(); | 
| 1700 |     else | 
| 1701 |         return d->m_bestFitLinePen; | 
| 1702 | } | 
| 1703 |  | 
| 1704 | /*! | 
| 1705 |     Stream operator for adding the data point \a point to the series. | 
| 1706 |     \sa append() | 
| 1707 | */ | 
| 1708 | QXYSeries &QXYSeries::operator<< (const QPointF &point) | 
| 1709 | { | 
| 1710 |     append(point); | 
| 1711 |     return *this; | 
| 1712 | } | 
| 1713 |  | 
| 1714 | /*! | 
| 1715 |     Stream operator for adding the list of data points specified by \a points | 
| 1716 |     to the series. | 
| 1717 |     \sa append() | 
| 1718 | */ | 
| 1719 |  | 
| 1720 | QXYSeries &QXYSeries::operator<< (const QList<QPointF>& points) | 
| 1721 | { | 
| 1722 |     append(points); | 
| 1723 |     return *this; | 
| 1724 | } | 
| 1725 |  | 
| 1726 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
| 1727 |  | 
| 1728 | QXYSeriesPrivate::QXYSeriesPrivate(QXYSeries *q) | 
| 1729 |     : QAbstractSeriesPrivate(q), | 
| 1730 |       m_pen(QChartPrivate::defaultPen()), | 
| 1731 |       m_brush(QChartPrivate::defaultBrush()), | 
| 1732 |       m_pointsVisible(false), | 
| 1733 |       m_pointLabelsFormat(QLatin1String("@xPoint, @yPoint" )), | 
| 1734 |       m_pointLabelsVisible(false), | 
| 1735 |       m_pointLabelsFont(QChartPrivate::defaultFont()), | 
| 1736 |       m_pointLabelsColor(QChartPrivate::defaultPen().color()), | 
| 1737 |       m_pointLabelsClipping(true), | 
| 1738 |       m_bestFitLinePen(QChartPrivate::defaultPen()), | 
| 1739 |       m_bestFitLineVisible(false), | 
| 1740 |       m_markerSize(15.0) | 
| 1741 | { | 
| 1742 | } | 
| 1743 |  | 
| 1744 | void QXYSeriesPrivate::initializeDomain() | 
| 1745 | { | 
| 1746 |     qreal minX(0); | 
| 1747 |     qreal minY(0); | 
| 1748 |     qreal maxX(1); | 
| 1749 |     qreal maxY(1); | 
| 1750 |  | 
| 1751 |     Q_Q(QXYSeries); | 
| 1752 |  | 
| 1753 |     const QList<QPointF> &points = q->points(); | 
| 1754 |  | 
| 1755 |     if (!points.isEmpty()) { | 
| 1756 |         minX = points[0].x(); | 
| 1757 |         minY = points[0].y(); | 
| 1758 |         maxX = minX; | 
| 1759 |         maxY = minY; | 
| 1760 |  | 
| 1761 |         for (int i = 0; i < points.size(); i++) { | 
| 1762 |             qreal x = points[i].x(); | 
| 1763 |             qreal y = points[i].y(); | 
| 1764 |             minX = qMin(a: minX, b: x); | 
| 1765 |             minY = qMin(a: minY, b: y); | 
| 1766 |             maxX = qMax(a: maxX, b: x); | 
| 1767 |             maxY = qMax(a: maxY, b: y); | 
| 1768 |         } | 
| 1769 |     } | 
| 1770 |  | 
| 1771 |     domain()->setRange(minX, maxX, minY, maxY); | 
| 1772 | } | 
| 1773 |  | 
| 1774 | QList<QLegendMarker*> QXYSeriesPrivate::createLegendMarkers(QLegend* legend) | 
| 1775 | { | 
| 1776 |     Q_Q(QXYSeries); | 
| 1777 |     QList<QLegendMarker*> list; | 
| 1778 |     return list << new QXYLegendMarker(q,legend); | 
| 1779 | } | 
| 1780 |  | 
| 1781 | void QXYSeriesPrivate::initializeAxes() | 
| 1782 | { | 
| 1783 |  | 
| 1784 | } | 
| 1785 |  | 
| 1786 | QAbstractAxis::AxisType QXYSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const | 
| 1787 | { | 
| 1788 |     Q_UNUSED(orientation); | 
| 1789 |     return QAbstractAxis::AxisTypeValue; | 
| 1790 | } | 
| 1791 |  | 
| 1792 | QAbstractAxis* QXYSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const | 
| 1793 | { | 
| 1794 |     Q_UNUSED(orientation); | 
| 1795 |     return new QValueAxis; | 
| 1796 | } | 
| 1797 |  | 
| 1798 | void QXYSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, | 
| 1799 |                                             int duration, QEasingCurve &curve) | 
| 1800 | { | 
| 1801 |     XYChart *item = static_cast<XYChart *>(m_item.get()); | 
| 1802 |     Q_ASSERT(item); | 
| 1803 |     if (item->animation()) | 
| 1804 |         item->animation()->stopAndDestroyLater(); | 
| 1805 |  | 
| 1806 |     if (options.testFlag(flag: QChart::SeriesAnimations)) | 
| 1807 |         item->setAnimation(new XYAnimation(item, duration, curve)); | 
| 1808 |     else | 
| 1809 |         item->setAnimation(0); | 
| 1810 |     QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); | 
| 1811 | } | 
| 1812 |  | 
| 1813 | void QXYSeriesPrivate::drawPointLabels(QPainter *painter, const QList<QPointF> &allPoints, | 
| 1814 |                                        const int offset) | 
| 1815 | { | 
| 1816 |     if (m_pointLabelsVisible || !m_pointsConfiguration.isEmpty()) { | 
| 1817 |         if (m_pointLabelsClipping) | 
| 1818 |             painter->setClipping(true); | 
| 1819 |         else | 
| 1820 |             painter->setClipping(false); | 
| 1821 |  | 
| 1822 |         QList<int> pointsToSkip; | 
| 1823 |         QHash<int, QString> labelFormats; | 
| 1824 |         QHash<int, int> offsets; | 
| 1825 |  | 
| 1826 |         if (!m_pointsConfiguration.isEmpty()) { | 
| 1827 |             for (int i = 0; i < allPoints.size(); ++i) { | 
| 1828 |                 bool drawLabel = m_pointLabelsVisible; | 
| 1829 |                 if (m_pointsConfiguration.contains(key: i)) { | 
| 1830 |                     const auto &conf = m_pointsConfiguration[i]; | 
| 1831 |                     auto key = QXYSeries::PointConfiguration::LabelVisibility; | 
| 1832 |                     if (conf.contains(key)) { | 
| 1833 |                         drawLabel = m_pointsConfiguration[i][key].toBool(); | 
| 1834 |                         key = QXYSeries::PointConfiguration::Size; | 
| 1835 |                         if (drawLabel && conf.contains(key)) | 
| 1836 |                             offsets[i] = conf[key].toReal(); | 
| 1837 |                     } | 
| 1838 |                     key = QXYSeries::PointConfiguration::LabelFormat; | 
| 1839 |                     if (conf.contains(key) && !conf[key].toString().isEmpty()) | 
| 1840 |                         labelFormats[i] = conf[key].toString(); | 
| 1841 |                 } | 
| 1842 |  | 
| 1843 |                 if (!drawLabel) | 
| 1844 |                     pointsToSkip << i; | 
| 1845 |             } | 
| 1846 |         } | 
| 1847 |  | 
| 1848 |         drawSeriesPointLabels(painter, points: allPoints, offset, offsets, indexesToSkip: pointsToSkip, customLabels: labelFormats); | 
| 1849 |     } | 
| 1850 | } | 
| 1851 |  | 
| 1852 | void QXYSeriesPrivate::drawSeriesPointLabels(QPainter *painter, const QList<QPointF> &points, | 
| 1853 |                                              const int offset, const QHash<int, int> &offsets, | 
| 1854 |                                              const QList<int> &indexesToSkip, | 
| 1855 |                                              const QHash<int, QString> &labelFormats) | 
| 1856 | { | 
| 1857 |     if (points.size() == 0) | 
| 1858 |         return; | 
| 1859 |  | 
| 1860 |     static const QString xPointTag(QLatin1String("@xPoint" )); | 
| 1861 |     static const QString yPointTag(QLatin1String("@yPoint" )); | 
| 1862 |     static const QString indexTag(QLatin1String("@index" )); | 
| 1863 |  | 
| 1864 |     QFont f(m_pointLabelsFont); | 
| 1865 |     f.setPixelSize(QFontInfo(m_pointLabelsFont).pixelSize()); | 
| 1866 |     painter->setFont(f); | 
| 1867 |     painter->setPen(QPen(m_pointLabelsColor)); | 
| 1868 |     QFontMetrics fm(painter->font()); | 
| 1869 |     // m_points is used for the label here as it has the series point information | 
| 1870 |     // points variable passed is used for positioning because it has the coordinates | 
| 1871 |     const int pointCount = qMin(a: points.size(), b: m_points.size()); | 
| 1872 |     for (int i(0); i < pointCount; i++) { | 
| 1873 |         if (indexesToSkip.contains(t: i)) | 
| 1874 |             continue; | 
| 1875 |  | 
| 1876 |         QString pointLabel = labelFormats.contains(key: i) ? labelFormats[i] : m_pointLabelsFormat; | 
| 1877 |         pointLabel.replace(before: xPointTag, after: presenter()->numberToString(value: m_points.at(i).x())); | 
| 1878 |         pointLabel.replace(before: yPointTag, after: presenter()->numberToString(value: m_points.at(i).y())); | 
| 1879 |         pointLabel.replace(before: indexTag, after: presenter()->numberToString(value: i)); | 
| 1880 |  | 
| 1881 |         int currOffset = offset; | 
| 1882 |         if (offsets.contains(key: i)) | 
| 1883 |             currOffset = offsets[i]; | 
| 1884 |  | 
| 1885 |         const int labelOffset = currOffset + 2; | 
| 1886 |  | 
| 1887 |         // Position text in relation to the point | 
| 1888 |         int pointLabelWidth = fm.horizontalAdvance(pointLabel); | 
| 1889 |         QPointF position(points.at(i)); | 
| 1890 |         position.setX(position.x() - pointLabelWidth / 2); | 
| 1891 |         position.setY(position.y() - labelOffset); | 
| 1892 |  | 
| 1893 |         painter->drawText(p: position, s: pointLabel); | 
| 1894 |     } | 
| 1895 | } | 
| 1896 |  | 
| 1897 | void QXYSeriesPrivate::drawBestFitLine(QPainter *painter, const QRectF &clipRect) | 
| 1898 | { | 
| 1899 |     bool ok = false; | 
| 1900 |     const auto &bestFitLineParams = bestFitLineEquation(ok); | 
| 1901 |  | 
| 1902 |     if (!ok) | 
| 1903 |         return; | 
| 1904 |  | 
| 1905 |     auto *domain = this->domain(); | 
| 1906 |     const auto clipOriginX = domain->isReverseX() ? clipRect.right() : clipRect.left(); | 
| 1907 |     const auto clipOriginY = domain->isReverseY() ? clipRect.top() : clipRect.bottom(); | 
| 1908 |     const auto domainOrigin = domain->calculateDomainPoint(point: {clipOriginX, clipOriginY}); | 
| 1909 |  | 
| 1910 |     const qreal x1 = domainOrigin.x(); | 
| 1911 |     const qreal y1 = bestFitLineParams.first * x1 + bestFitLineParams.second; | 
| 1912 |     QPointF p1 = domain->calculateGeometryPoint(point: QPointF(x1, y1), ok); | 
| 1913 |  | 
| 1914 |     const qreal x2 = domainOrigin.x() + 1; | 
| 1915 |     const qreal y2 = bestFitLineParams.first * x2 + bestFitLineParams.second; | 
| 1916 |     QPointF p2 = domain->calculateGeometryPoint(point: QPointF(x2, y2), ok); | 
| 1917 |  | 
| 1918 |     if (ok) { | 
| 1919 |         QLineF bestFitLine { p1, p2 }; | 
| 1920 |         // maxLength is length of the diagonal, no line can be longer | 
| 1921 |         const qreal maxLength = qSqrt(v: qPow(x: clipRect.width(), y: 2) * qPow(x: clipRect.height(), y: 2)); | 
| 1922 |         bestFitLine.setLength(maxLength); | 
| 1923 |  | 
| 1924 |         painter->save(); | 
| 1925 |         painter->setPen(m_bestFitLinePen); | 
| 1926 |         painter->drawLine(l: bestFitLine); | 
| 1927 |         painter->restore(); | 
| 1928 |     } | 
| 1929 | } | 
| 1930 |  | 
| 1931 | void QXYSeries::setBestFitLineColor(const QColor &color) | 
| 1932 | { | 
| 1933 |     QPen p = bestFitLinePen(); | 
| 1934 |     if (p.color() != color) { | 
| 1935 |         p.setColor(color); | 
| 1936 |         setBestFitLinePen(p); | 
| 1937 |     } | 
| 1938 | } | 
| 1939 |  | 
| 1940 | QColor QXYSeries::bestFitLineColor() const | 
| 1941 | { | 
| 1942 |     return bestFitLinePen().color(); | 
| 1943 | } | 
| 1944 |  | 
| 1945 | QPair<qreal, qreal> QXYSeriesPrivate::bestFitLineEquation(bool &ok) const | 
| 1946 | { | 
| 1947 |     if (m_points.size() <= 1) { | 
| 1948 |         ok = false; | 
| 1949 |         return { 0, 0 }; | 
| 1950 |     } | 
| 1951 |  | 
| 1952 |     ok = true; | 
| 1953 |     qreal xSum = 0.0, x2Sum = 0.0, ySum = 0.0, xySum = 0.0; | 
| 1954 |     for (const auto &point : m_points) { | 
| 1955 |         xSum += point.x(); | 
| 1956 |         ySum += point.y(); | 
| 1957 |         x2Sum += qPow(x: point.x(), y: 2); | 
| 1958 |         xySum += point.x() * point.y(); | 
| 1959 |     } | 
| 1960 |  | 
| 1961 |     const qreal divisor = m_points.size() * x2Sum - xSum * xSum; | 
| 1962 |     // To prevent crashes in unusual cases | 
| 1963 |     if (divisor == 0.0) { | 
| 1964 |         ok = false; | 
| 1965 |         return { 0, 0 }; | 
| 1966 |     } | 
| 1967 |  | 
| 1968 |     qreal a = (m_points.size() * xySum - xSum * ySum) / divisor; | 
| 1969 |     qreal b = (x2Sum * ySum - xSum * xySum) / divisor; | 
| 1970 |  | 
| 1971 |     return { a, b }; | 
| 1972 | } | 
| 1973 |  | 
| 1974 | void QXYSeriesPrivate::setPointSelected(int index, bool selected, bool &callSignal) | 
| 1975 | { | 
| 1976 |     if (index < 0 || index > m_points.size() - 1) | 
| 1977 |         return; | 
| 1978 |  | 
| 1979 |     if (selected) { | 
| 1980 |         if (!isPointSelected(index)) { | 
| 1981 |             m_selectedPoints << index; | 
| 1982 |             callSignal = true; | 
| 1983 |         } | 
| 1984 |     } else { | 
| 1985 |         if (isPointSelected(index)) { | 
| 1986 |             m_selectedPoints.remove(value: index); | 
| 1987 |             callSignal = true; | 
| 1988 |         } | 
| 1989 |     } | 
| 1990 | } | 
| 1991 |  | 
| 1992 | bool QXYSeriesPrivate::isPointSelected(int index) | 
| 1993 | { | 
| 1994 |     return m_selectedPoints.contains(value: index); | 
| 1995 | } | 
| 1996 |  | 
| 1997 | bool QXYSeriesPrivate::isMarkerSizeDefault() | 
| 1998 | { | 
| 1999 |     return m_markerSizeDefault; | 
| 2000 | } | 
| 2001 |  | 
| 2002 | void QXYSeriesPrivate::setMarkerSize(qreal markerSize) | 
| 2003 | { | 
| 2004 |     m_markerSize = markerSize; | 
| 2005 | } | 
| 2006 |  | 
| 2007 | QList<qreal> QXYSeriesPrivate::colorByData() const | 
| 2008 | { | 
| 2009 |     return m_colorByData; | 
| 2010 | } | 
| 2011 |  | 
| 2012 | bool QXYSeriesPrivate::setPointConfiguration(const int index, | 
| 2013 |                                              const QXYSeries::PointConfiguration key, | 
| 2014 |                                              const QVariant &value) | 
| 2015 | { | 
| 2016 |     QHash<QXYSeries::PointConfiguration, QVariant> conf; | 
| 2017 |     if (m_pointsConfiguration.contains(key: index)) | 
| 2018 |         conf = m_pointsConfiguration[index]; | 
| 2019 |  | 
| 2020 |     bool changed = false; | 
| 2021 |     if (conf.contains(key)) { | 
| 2022 |         if (conf[key] != value) | 
| 2023 |             changed = true; | 
| 2024 |     } else { | 
| 2025 |         changed = true; | 
| 2026 |     } | 
| 2027 |  | 
| 2028 |     conf[key] = value; | 
| 2029 |     m_pointsConfiguration[index] = conf; | 
| 2030 |  | 
| 2031 |     return changed; | 
| 2032 | } | 
| 2033 |  | 
| 2034 | QT_END_NAMESPACE | 
| 2035 |  | 
| 2036 | #include "moc_qxyseries.cpp" | 
| 2037 | #include "moc_qxyseries_p.cpp" | 
| 2038 |  |