| 1 | // Copyright (C) 2021 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 3 | |
| 4 | #include <QtCharts/QScatterSeries> |
| 5 | #include <private/qscatterseries_p.h> |
| 6 | #include <private/scatterchartitem_p.h> |
| 7 | #include <private/chartdataset_p.h> |
| 8 | #include <private/charttheme_p.h> |
| 9 | #include <private/scatteranimation_p.h> |
| 10 | #include <private/qchart_p.h> |
| 11 | |
| 12 | /*! |
| 13 | \class QScatterSeries |
| 14 | \inmodule QtCharts |
| 15 | \brief The QScatterSeries class presents data in scatter charts. |
| 16 | |
| 17 | The scatter data is displayed as a collection of points on the chart. For |
| 18 | each point, two values are specified that determine its position on the |
| 19 | horizontal axis and the vertical axis. |
| 20 | |
| 21 | \image examples_scatterchart.png |
| 22 | |
| 23 | The following code snippet illustrates how to create a basic scatter chart: |
| 24 | \code |
| 25 | QScatterSeries* series = new QScatterSeries(); |
| 26 | series->append(0, 6); |
| 27 | series->append(2, 4); |
| 28 | ... |
| 29 | chart->addSeries(series); |
| 30 | \endcode |
| 31 | |
| 32 | For more information, see \l{Charts with Widgets Gallery} and |
| 33 | \l {Creating Scatter Charts}. |
| 34 | */ |
| 35 | /*! |
| 36 | \qmltype ScatterSeries |
| 37 | \nativetype QScatterSeries |
| 38 | \inqmlmodule QtCharts |
| 39 | |
| 40 | \inherits XYSeries |
| 41 | |
| 42 | \brief The ScatterSeries type presents data in scatter charts. |
| 43 | |
| 44 | The scatter data is displayed as a collection of points on the chart. For |
| 45 | each point, two values are specified that determine its position on the |
| 46 | horizontal axis and the vertical axis. |
| 47 | |
| 48 | \image examples_qmlchart5.png |
| 49 | |
| 50 | The following QML code shows how to create a chart with two simple scatter |
| 51 | series: |
| 52 | \snippet qmlchartsgallery/qml/ScatterSeries.qml 1 |
| 53 | |
| 54 | For more information, see \l{Charts with QML Gallery}. |
| 55 | */ |
| 56 | |
| 57 | /*! |
| 58 | \enum QScatterSeries::MarkerShape |
| 59 | |
| 60 | This enum value describes the shape used when rendering marker items. |
| 61 | |
| 62 | \value MarkerShapeCircle |
| 63 | The marker is a circle. This is the default value. |
| 64 | \value MarkerShapeRectangle |
| 65 | The marker is a rectangle. |
| 66 | \value MarkerShapeRotatedRectangle |
| 67 | The marker is a rotated rectangle. |
| 68 | \value MarkerShapeTriangle |
| 69 | The marker is a triangle. |
| 70 | \value MarkerShapeStar |
| 71 | The marker is a star. |
| 72 | \value MarkerShapePentagon |
| 73 | The marker is a pentagon. |
| 74 | */ |
| 75 | |
| 76 | /*! |
| 77 | \property QScatterSeries::brush |
| 78 | \brief The brush used to draw the scatter series markers. |
| 79 | |
| 80 | The brush can be an image that can be created using QPainterPath, |
| 81 | for example. |
| 82 | */ |
| 83 | |
| 84 | /*! |
| 85 | \qmlproperty brush ScatterSeries::brush |
| 86 | The brush used to draw the scatter series markers. |
| 87 | */ |
| 88 | |
| 89 | /*! |
| 90 | \property QScatterSeries::color |
| 91 | \brief The color used to fill the series markers. |
| 92 | |
| 93 | This is a convenience property for modifying the color of the brush. |
| 94 | \sa QScatterSeries::brush() |
| 95 | */ |
| 96 | |
| 97 | /*! |
| 98 | \property QScatterSeries::borderColor |
| 99 | \brief The color used to draw the marker borders. |
| 100 | |
| 101 | This is a convenience property for modifying the color of the pen. |
| 102 | \sa QScatterSeries::pen() |
| 103 | */ |
| 104 | |
| 105 | /*! |
| 106 | \qmlproperty int ScatterSeries::count |
| 107 | The number of data points in the series. |
| 108 | */ |
| 109 | |
| 110 | /*! |
| 111 | \qmlproperty color ScatterSeries::borderColor |
| 112 | The color used to draw the marker borders. |
| 113 | */ |
| 114 | |
| 115 | /*! |
| 116 | \qmlproperty real ScatterSeries::borderWidth |
| 117 | The width of the border line. By default, the width is 2.0. |
| 118 | */ |
| 119 | |
| 120 | /*! |
| 121 | \property QScatterSeries::markerShape |
| 122 | \brief The shape of the marker used to render the points in the series. |
| 123 | |
| 124 | The default shape is MarkerShapeCircle. |
| 125 | |
| 126 | \sa MarkerShape |
| 127 | */ |
| 128 | /*! |
| 129 | \qmlproperty enumeration ScatterSeries::markerShape |
| 130 | |
| 131 | The shape used when rendering marker items: |
| 132 | |
| 133 | \value ScatterSeries.MarkerShapeCircle |
| 134 | The marker is a circle. This is the default value. |
| 135 | \value ScatterSeries.MarkerShapeRectangle |
| 136 | The marker is a rectangle. |
| 137 | */ |
| 138 | |
| 139 | /*! |
| 140 | \property QScatterSeries::markerSize |
| 141 | \brief The size of the marker used to render the points in the series. |
| 142 | |
| 143 | \sa QXYSeries::setMarkerSize |
| 144 | */ |
| 145 | /*! |
| 146 | \qmlproperty real ScatterSeries::markerSize |
| 147 | The size of the marker used to render the points in the series. |
| 148 | */ |
| 149 | |
| 150 | /*! |
| 151 | \qmlproperty string ScatterSeries::brushFilename |
| 152 | The name of the file used as a brush for the series. |
| 153 | */ |
| 154 | |
| 155 | /*! |
| 156 | \fn void QScatterSeries::colorChanged(QColor color) |
| 157 | This signal is emitted when the fill (brush) color changes to \a color. |
| 158 | */ |
| 159 | |
| 160 | /*! |
| 161 | \fn void QScatterSeries::borderColorChanged(QColor color) |
| 162 | This signal is emitted when the line (pen) color changes to \a color. |
| 163 | */ |
| 164 | |
| 165 | /*! |
| 166 | \fn void QScatterSeries::markerShapeChanged(MarkerShape shape) |
| 167 | This signal is emitted when the marker shape changes to \a shape. |
| 168 | */ |
| 169 | |
| 170 | /*! |
| 171 | \fn void QScatterSeries::markerSizeChanged(qreal size) |
| 172 | This signal is emitted when the marker size changes to \a size. |
| 173 | */ |
| 174 | |
| 175 | QT_BEGIN_NAMESPACE |
| 176 | |
| 177 | /*! |
| 178 | Constructs a series object that is a child of \a parent. |
| 179 | */ |
| 180 | QScatterSeries::QScatterSeries(QObject *parent) |
| 181 | : QXYSeries(*new QScatterSeriesPrivate(this), parent) |
| 182 | { |
| 183 | setPointsVisible(true); |
| 184 | |
| 185 | // Emit QScatterSeries' markerSizeChanged signal as it's not the same as |
| 186 | // QXYSeries' markerSizeChanged |
| 187 | connect(sender: this, signal: &QXYSeries::markerSizeChanged, context: this, slot: &QScatterSeries::markerSizeChanged); |
| 188 | } |
| 189 | |
| 190 | /*! |
| 191 | Deletes the scatter series. |
| 192 | |
| 193 | \note Adding the series to QChart transfers the ownership to the chart. |
| 194 | */ |
| 195 | QScatterSeries::~QScatterSeries() |
| 196 | { |
| 197 | Q_D(QScatterSeries); |
| 198 | if (d->m_chart) |
| 199 | d->m_chart->removeSeries(series: this); |
| 200 | } |
| 201 | |
| 202 | /*! |
| 203 | \reimp |
| 204 | */ |
| 205 | QAbstractSeries::SeriesType QScatterSeries::type() const |
| 206 | { |
| 207 | return QAbstractSeries::SeriesTypeScatter; |
| 208 | } |
| 209 | |
| 210 | /*! |
| 211 | \reimp |
| 212 | */ |
| 213 | void QScatterSeries::setPen(const QPen &pen) |
| 214 | { |
| 215 | Q_D(QXYSeries); |
| 216 | if (d->m_pen != pen) { |
| 217 | bool emitColorChanged = d->m_pen.color() != pen.color(); |
| 218 | d->m_pen = pen; |
| 219 | emit d->seriesUpdated(); |
| 220 | if (emitColorChanged) |
| 221 | emit borderColorChanged(color: pen.color()); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | /*! |
| 226 | \reimp |
| 227 | */ |
| 228 | void QScatterSeries::setBrush(const QBrush &brush) |
| 229 | { |
| 230 | Q_D(QScatterSeries); |
| 231 | if (d->m_brush != brush) { |
| 232 | bool emitColorChanged = d->m_brush.color() != brush.color(); |
| 233 | d->m_brush = brush; |
| 234 | emit d->seriesUpdated(); |
| 235 | if (emitColorChanged) |
| 236 | emit colorChanged(color: brush.color()); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | QBrush QScatterSeries::brush() const |
| 241 | { |
| 242 | Q_D(const QScatterSeries); |
| 243 | if (d->m_brush == QChartPrivate::defaultBrush()) |
| 244 | return QBrush(); |
| 245 | else |
| 246 | return d->m_brush; |
| 247 | } |
| 248 | |
| 249 | void QScatterSeries::setColor(const QColor &color) |
| 250 | { |
| 251 | QBrush b = brush(); |
| 252 | if (b == QChartPrivate::defaultBrush()) |
| 253 | b = QBrush(); |
| 254 | if (b == QBrush()) |
| 255 | b.setStyle(Qt::SolidPattern); |
| 256 | b.setColor(color); |
| 257 | setBrush(b); |
| 258 | } |
| 259 | |
| 260 | QColor QScatterSeries::color() const |
| 261 | { |
| 262 | return brush().color(); |
| 263 | } |
| 264 | |
| 265 | void QScatterSeries::setBorderColor(const QColor &color) |
| 266 | { |
| 267 | QPen p = pen(); |
| 268 | if (p == QChartPrivate::defaultPen()) |
| 269 | p = QPen(); |
| 270 | p.setColor(color); |
| 271 | setPen(p); |
| 272 | } |
| 273 | |
| 274 | QColor QScatterSeries::borderColor() const |
| 275 | { |
| 276 | return pen().color(); |
| 277 | } |
| 278 | |
| 279 | QScatterSeries::MarkerShape QScatterSeries::markerShape() const |
| 280 | { |
| 281 | Q_D(const QScatterSeries); |
| 282 | return d->m_shape; |
| 283 | } |
| 284 | |
| 285 | void QScatterSeries::setMarkerShape(MarkerShape shape) |
| 286 | { |
| 287 | Q_D(QScatterSeries); |
| 288 | if (d->m_shape != shape) { |
| 289 | d->m_shape = shape; |
| 290 | emit d->seriesUpdated(); |
| 291 | emit markerShapeChanged(shape); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | qreal QScatterSeries::markerSize() const |
| 296 | { |
| 297 | // markerSize has moved to QXYSeries, but this method needs to remain for API compatibility. |
| 298 | return QXYSeries::markerSize(); |
| 299 | } |
| 300 | void QScatterSeries::setMarkerSize(qreal size) |
| 301 | { |
| 302 | // markerSize has moved to QXYSeries, but this method needs to remain for API compatibility. |
| 303 | QXYSeries::setMarkerSize(size); |
| 304 | } |
| 305 | |
| 306 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 307 | |
| 308 | QScatterSeriesPrivate::QScatterSeriesPrivate(QScatterSeries *q) |
| 309 | : QXYSeriesPrivate(q), |
| 310 | m_shape(QScatterSeries::MarkerShapeCircle) |
| 311 | { |
| 312 | } |
| 313 | |
| 314 | void QScatterSeriesPrivate::initializeGraphics(QGraphicsItem* parent) |
| 315 | { |
| 316 | Q_Q(QScatterSeries); |
| 317 | ScatterChartItem *scatter = new ScatterChartItem(q,parent); |
| 318 | m_item.reset(p: scatter); |
| 319 | QAbstractSeriesPrivate::initializeGraphics(parent); |
| 320 | } |
| 321 | |
| 322 | void QScatterSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced) |
| 323 | { |
| 324 | Q_Q(QScatterSeries); |
| 325 | const QList<QColor> colors = theme->seriesColors(); |
| 326 | const QList<QGradient> gradients = theme->seriesGradients(); |
| 327 | |
| 328 | if (forced || QChartPrivate::defaultPen() == m_pen) { |
| 329 | QPen pen; |
| 330 | pen.setColor(ChartThemeManager::colorAt(gradient: gradients.at(i: index % gradients.size()), pos: 0.0)); |
| 331 | pen.setWidthF(2); |
| 332 | q->setPen(pen); |
| 333 | } |
| 334 | |
| 335 | if (forced || QChartPrivate::defaultBrush() == m_brush) { |
| 336 | QBrush brush(colors.at(i: index % colors.size())); |
| 337 | q->setBrush(brush); |
| 338 | } |
| 339 | |
| 340 | if (forced || QChartPrivate::defaultPen().color() == m_pointLabelsColor) { |
| 341 | QColor color = theme->labelBrush().color(); |
| 342 | q->setPointLabelsColor(color); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | void QScatterSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, int duration, |
| 347 | QEasingCurve &curve) |
| 348 | { |
| 349 | ScatterChartItem *item = static_cast<ScatterChartItem *>(m_item.get()); |
| 350 | Q_ASSERT(item); |
| 351 | |
| 352 | if (item->animation()) |
| 353 | item->animation()->stopAndDestroyLater(); |
| 354 | |
| 355 | if (options.testFlag(flag: QChart::SeriesAnimations)) |
| 356 | item->setAnimation(new ScatterAnimation(item, duration, curve)); |
| 357 | else |
| 358 | item->setAnimation(0); |
| 359 | |
| 360 | QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); |
| 361 | } |
| 362 | |
| 363 | QT_END_NAMESPACE |
| 364 | |
| 365 | #include "moc_qscatterseries.cpp" |
| 366 | |