| 1 | // Copyright (C) 2016 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/QPieSeries> | 
| 7 | #include <private/qpieseries_p.h> | 
| 8 | #include <QtCharts/QPieSlice> | 
| 9 | #include <private/qpieslice_p.h> | 
| 10 | #include <private/pieslicedata_p.h> | 
| 11 | #include <private/chartdataset_p.h> | 
| 12 | #include <private/charttheme_p.h> | 
| 13 | #include <QtCharts/QAbstractAxis> | 
| 14 | #include <private/pieanimation_p.h> | 
| 15 | #include <private/charthelpers_p.h> | 
| 16 |  | 
| 17 | #include <QtCharts/QPieLegendMarker> | 
| 18 |  | 
| 19 | QT_BEGIN_NAMESPACE | 
| 20 |  | 
| 21 | /*! | 
| 22 |     \class QPieSeries | 
| 23 |     \inmodule QtCharts | 
| 24 |     \brief The QPieSeries class presents data in pie charts. | 
| 25 |  | 
| 26 |     A pie series consists of slices that are defined as QPieSlice objects. | 
| 27 |     The slices can have any values as the QPieSeries object calculates | 
| 28 |     the percentage of a slice compared with the sum of all slices in the series | 
| 29 |     to determine the actual size of the slice in the chart. | 
| 30 |  | 
| 31 |     Pie size and position on the chart are controlled by using relative values | 
| 32 |     that range from 0.0 to 1.0. | 
| 33 |     These relate to the actual chart rectangle. | 
| 34 |  | 
| 35 |     By default, the pie is defined as a full pie. A partial pie can be created | 
| 36 |     by setting a starting angle and angle span for the series. | 
| 37 |     A full pie is 360 degrees, where 0 is at 12 a'clock. | 
| 38 |  | 
| 39 |     See the \l {Charts with Widgets Gallery} to learn how to use QPieSeries. | 
| 40 |     \image examples_piechart.png | 
| 41 |     \image examples_donutchart.png | 
| 42 |  | 
| 43 |     \sa QPieSlice, QChart | 
| 44 | */ | 
| 45 | /*! | 
| 46 |     \qmltype PieSeries | 
| 47 |     \nativetype QPieSeries | 
| 48 |     \inqmlmodule QtCharts | 
| 49 |  | 
| 50 |     \inherits AbstractSeries | 
| 51 |  | 
| 52 |     \brief Presents data in pie charts. | 
| 53 |  | 
| 54 |     A pie series consists of slices that are defined using the PieSlice type. | 
| 55 |     The slices can have any values as the PieSeries type calculates | 
| 56 |     the percentage of a slice compared with the sum of all slices in the series | 
| 57 |     to determine the actual size of the slice in the chart. | 
| 58 |  | 
| 59 |     Pie size and position on the chart are controlled by using relative values | 
| 60 |     that range from 0.0 to 1.0. | 
| 61 |     These relate to the actual chart rectangle. | 
| 62 |  | 
| 63 |     By default, the pie is defined as a full pie. A partial pie can be created | 
| 64 |     by setting a starting angle and angle span for the series. | 
| 65 |     A full pie is 360 degrees, where 0 is at 12 a'clock. | 
| 66 |  | 
| 67 |     The following QML example shows how to create a simple pie chart. | 
| 68 |  | 
| 69 |     \snippet qmlchartsgallery/qml/PieChart.qml 1 | 
| 70 |  | 
| 71 |     \beginfloatleft | 
| 72 |     \image examples_qmlchart1.png | 
| 73 |     \endfloat | 
| 74 |     \clearfloat | 
| 75 |  | 
| 76 |     \sa PieSlice, ChartView | 
| 77 | */ | 
| 78 |  | 
| 79 | /*! | 
| 80 |     \property QPieSeries::horizontalPosition | 
| 81 |     \brief The horizontal position of the pie. | 
| 82 |  | 
| 83 |     The value is relative to the chart rectangle, so that: | 
| 84 |  | 
| 85 |     \list | 
| 86 |     \li 0.0 is the absolute left. | 
| 87 |     \li 1.0 is the absolute right. | 
| 88 |     \endlist | 
| 89 |     The default value is 0.5 (center). | 
| 90 |     \sa verticalPosition | 
| 91 | */ | 
| 92 |  | 
| 93 | /*! | 
| 94 |     \qmlproperty real PieSeries::horizontalPosition | 
| 95 |  | 
| 96 |     The horizontal position of the pie. | 
| 97 |  | 
| 98 |     The value is relative to the chart rectangle, so that: | 
| 99 |  | 
| 100 |     \list | 
| 101 |     \li 0.0 is the absolute left. | 
| 102 |     \li 1.0 is the absolute right. | 
| 103 |     \endlist | 
| 104 |     The default value is 0.5 (center). | 
| 105 |     \sa verticalPosition | 
| 106 | */ | 
| 107 |  | 
| 108 | /*! | 
| 109 |     \property QPieSeries::verticalPosition | 
| 110 |     \brief The vertical position of the pie. | 
| 111 |  | 
| 112 |     The value is relative to the chart rectangle, so that: | 
| 113 |  | 
| 114 |     \list | 
| 115 |     \li 0.0 is the absolute top. | 
| 116 |     \li 1.0 is the absolute bottom. | 
| 117 |     \endlist | 
| 118 |     The default value is 0.5 (center). | 
| 119 |     \sa horizontalPosition | 
| 120 | */ | 
| 121 |  | 
| 122 | /*! | 
| 123 |     \qmlproperty real PieSeries::verticalPosition | 
| 124 |  | 
| 125 |     The vertical position of the pie. | 
| 126 |  | 
| 127 |     The value is relative to the chart rectangle, so that: | 
| 128 |  | 
| 129 |     \list | 
| 130 |     \li 0.0 is the absolute top. | 
| 131 |     \li 1.0 is the absolute bottom. | 
| 132 |     \endlist | 
| 133 |     The default value is 0.5 (center). | 
| 134 |     \sa horizontalPosition | 
| 135 | */ | 
| 136 |  | 
| 137 | /*! | 
| 138 |     \property QPieSeries::size | 
| 139 |     \brief The pie size. | 
| 140 |  | 
| 141 |     The value is relative to the chart rectangle, so that: | 
| 142 |  | 
| 143 |     \list | 
| 144 |     \li 0.0 is the minimum size (pie not drawn). | 
| 145 |     \li 1.0 is the maximum size that can fit the chart. | 
| 146 |     \endlist | 
| 147 |  | 
| 148 |     When setting this property, the holeSize property is adjusted if necessary, | 
| 149 |     to ensure that the hole size is not greater than the pie size. | 
| 150 |  | 
| 151 |     The default value is 0.7. | 
| 152 | */ | 
| 153 |  | 
| 154 | /*! | 
| 155 |     \qmlproperty real PieSeries::size | 
| 156 |  | 
| 157 |     The pie size. | 
| 158 |  | 
| 159 |     The value is relative to the chart rectangle, so that: | 
| 160 |  | 
| 161 |     \list | 
| 162 |     \li 0.0 is the minimum size (pie not drawn). | 
| 163 |     \li 1.0 is the maximum size that can fit the chart. | 
| 164 |     \endlist | 
| 165 |  | 
| 166 |     When setting this property, the holeSize property is adjusted if necessary, | 
| 167 |     to ensure that the hole size is not greater than the pie size. | 
| 168 |  | 
| 169 |     The default value is 0.7. | 
| 170 | */ | 
| 171 |  | 
| 172 | /*! | 
| 173 |     \property QPieSeries::holeSize | 
| 174 |     \brief The donut hole size. | 
| 175 |  | 
| 176 |     The value is relative to the chart rectangle, so that: | 
| 177 |  | 
| 178 |     \list | 
| 179 |     \li 0.0 is the minimum size (full pie drawn without a hole). | 
| 180 |     \li 1.0 is the maximum size that can fit the chart (the donut has no width). | 
| 181 |     \endlist | 
| 182 |  | 
| 183 |     When setting this property, the \l size property is adjusted if necessary, | 
| 184 |     to ensure that the hole size is not greater than the pie size. | 
| 185 |  | 
| 186 |     The default value is 0.0. | 
| 187 | */ | 
| 188 |  | 
| 189 | /*! | 
| 190 |     \qmlproperty real PieSeries::holeSize | 
| 191 |  | 
| 192 |     The donut hole size. | 
| 193 |  | 
| 194 |     The value is relative to the chart rectangle, so that: | 
| 195 |  | 
| 196 |     \list | 
| 197 |     \li 0.0 is the minimum size (full pie drawn without a hole). | 
| 198 |     \li 1.0 is the maximum size that can fit the chart (the donut has no width). | 
| 199 |     \endlist | 
| 200 |  | 
| 201 |     When setting this property, the \l size property is adjusted if necessary, | 
| 202 |     to ensure that the hole size is not greater than the pie size. | 
| 203 |  | 
| 204 |     The default value is 0.0. | 
| 205 | */ | 
| 206 |  | 
| 207 | /*! | 
| 208 |     \property QPieSeries::startAngle | 
| 209 |     \brief The starting angle of the pie. | 
| 210 |  | 
| 211 |     A full pie is 360 degrees, where 0 degrees is at 12 a'clock. | 
| 212 |  | 
| 213 |     The default value is 0. | 
| 214 | */ | 
| 215 |  | 
| 216 | /*! | 
| 217 |     \qmlproperty real PieSeries::startAngle | 
| 218 |  | 
| 219 |     The starting angle of the pie. | 
| 220 |  | 
| 221 |     A full pie is 360 degrees, where 0 degrees is at 12 a'clock. | 
| 222 |  | 
| 223 |     The default value is 0. | 
| 224 | */ | 
| 225 |  | 
| 226 | /*! | 
| 227 |     \property QPieSeries::endAngle | 
| 228 |     \brief The ending angle of the pie. | 
| 229 |  | 
| 230 |     A full pie is 360 degrees, where 0 degrees is at 12 a'clock. | 
| 231 |  | 
| 232 |     The default value is 360. | 
| 233 | */ | 
| 234 |  | 
| 235 | /*! | 
| 236 |     \qmlproperty real PieSeries::endAngle | 
| 237 |  | 
| 238 |     The ending angle of the pie. | 
| 239 |  | 
| 240 |     A full pie is 360 degrees, where 0 degrees is at 12 a'clock. | 
| 241 |  | 
| 242 |     The default value is 360. | 
| 243 | */ | 
| 244 |  | 
| 245 | /*! | 
| 246 |     \property QPieSeries::count | 
| 247 |  | 
| 248 |     \brief The number of slices in the series. | 
| 249 | */ | 
| 250 |  | 
| 251 | /*! | 
| 252 |     \qmlproperty int PieSeries::count | 
| 253 |  | 
| 254 |     The number of slices in the series. | 
| 255 | */ | 
| 256 |  | 
| 257 | /*! | 
| 258 |     \fn void QPieSeries::countChanged() | 
| 259 |     This signal is emitted when the slice count changes. | 
| 260 |     \sa count | 
| 261 | */ | 
| 262 |  | 
| 263 | /*! | 
| 264 |     \property QPieSeries::sum | 
| 265 |  | 
| 266 |     \brief The sum of all slices. | 
| 267 |  | 
| 268 |     The series keeps track of the sum of all the slices it holds. | 
| 269 | */ | 
| 270 |  | 
| 271 | /*! | 
| 272 |     \qmlproperty real PieSeries::sum | 
| 273 |  | 
| 274 |     The sum of all slices. | 
| 275 |  | 
| 276 |     The series keeps track of the sum of all the slices it holds. | 
| 277 | */ | 
| 278 |  | 
| 279 | /*! | 
| 280 |     \fn void QPieSeries::sumChanged() | 
| 281 |     This signal is emitted when the sum of all slices changes. | 
| 282 |     \sa sum | 
| 283 | */ | 
| 284 |  | 
| 285 | /*! | 
| 286 |     \fn void QPieSeries::added(const QList<QPieSlice*> &slices) | 
| 287 |  | 
| 288 |     This signal is emitted when the slices specified by \a slices are added to the series. | 
| 289 |  | 
| 290 |     \sa append(), insert() | 
| 291 | */ | 
| 292 | /*! | 
| 293 |     \qmlsignal PieSeries::added(list<PieSlice> slices) | 
| 294 |     This signal is emitted when the slices specified by \a slices are added to the series. | 
| 295 |  | 
| 296 |     The corresponding signal handler is \c onAdded. | 
| 297 | */ | 
| 298 |  | 
| 299 | /*! | 
| 300 |     \fn void QPieSeries::removed(const QList<QPieSlice*> &slices) | 
| 301 |     This signal is emitted when the slices specified by \a slices are removed from the series. | 
| 302 |     \sa remove() | 
| 303 | */ | 
| 304 | /*! | 
| 305 |     \qmlsignal PieSeries::removed(list<PieSlice> slices) | 
| 306 |     This signal is emitted when the slices specified by \a slices are removed from the series. | 
| 307 |  | 
| 308 |     The corresponding signal handler is \c onRemoved. | 
| 309 | */ | 
| 310 |  | 
| 311 | /*! | 
| 312 |     \qmlsignal PieSeries::sliceAdded(PieSlice slice) | 
| 313 |     This signal is emitted when the slice specified by \a slice is added to the series. | 
| 314 |  | 
| 315 |     The corresponding signal handler is \c onSliceAdded. | 
| 316 | */ | 
| 317 |  | 
| 318 | /*! | 
| 319 |     \qmlsignal PieSeries::sliceRemoved(PieSlice slice) | 
| 320 |     This signal is emitted when the slice specified by \a slice is removed from the series. | 
| 321 |  | 
| 322 |     The corresponding signal handler is \c onSliceRemoved. | 
| 323 | */ | 
| 324 |  | 
| 325 | /*! | 
| 326 |     \fn void QPieSeries::clicked(QPieSlice *slice) | 
| 327 |     This signal is emitted when the slice specified by \a slice is clicked. | 
| 328 |     \sa QPieSlice::clicked() | 
| 329 | */ | 
| 330 | /*! | 
| 331 |   \qmlsignal PieSeries::clicked(PieSlice slice) | 
| 332 |   This signal is emitted when the slice specified by \a slice is clicked. | 
| 333 |  | 
| 334 |   The corresponding signal handler is \c onClicked. | 
| 335 | */ | 
| 336 |  | 
| 337 | /*! | 
| 338 |     \fn void QPieSeries::pressed(QPieSlice *slice) | 
| 339 |     This signal is emitted when the user clicks the slice specified by \a slice | 
| 340 |     and holds down the mouse button. | 
| 341 |     \sa QPieSlice::pressed() | 
| 342 | */ | 
| 343 | /*! | 
| 344 |     \qmlsignal PieSeries::pressed(PieSlice slice) | 
| 345 |     This signal is emitted when the user clicks the slice specified by \a slice | 
| 346 |     and holds down the mouse button. | 
| 347 |  | 
| 348 |     The corresponding signal handler is \c onPressed. | 
| 349 | */ | 
| 350 |  | 
| 351 | /*! | 
| 352 |     \fn void QPieSeries::released(QPieSlice *slice) | 
| 353 |     This signal is emitted when the user releases the mouse press on the slice | 
| 354 |     specified by \a slice. | 
| 355 |     \sa QPieSlice::released() | 
| 356 | */ | 
| 357 | /*! | 
| 358 |     \qmlsignal PieSeries::released(PieSlice slice) | 
| 359 |     This signal is emitted when the user releases the mouse press on the slice | 
| 360 |     specified by \a slice. | 
| 361 |  | 
| 362 |     The corresponding signal handler is \c onReleased. | 
| 363 | */ | 
| 364 |  | 
| 365 | /*! | 
| 366 |     \fn void QPieSeries::doubleClicked(QPieSlice *slice) | 
| 367 |     This signal is emitted when the slice specified by \a slice is double-clicked. | 
| 368 |     \sa QPieSlice::doubleClicked() | 
| 369 | */ | 
| 370 | /*! | 
| 371 |     \qmlsignal PieSeries::doubleClicked(PieSlice slice) | 
| 372 |     This signal is emitted when the slice specified by \a slice is double-clicked. | 
| 373 |  | 
| 374 |     The corresponding signal handler is \c onDoubleClicked. | 
| 375 | */ | 
| 376 |  | 
| 377 | /*! | 
| 378 |     \fn void QPieSeries::hovered(QPieSlice* slice, bool state) | 
| 379 |     This signal is emitted when a mouse is hovered over the slice specified by | 
| 380 |     \a slice. When the mouse moves over the slice, \a state turns \c true, and | 
| 381 |     when the mouse moves away again, it turns \c false. | 
| 382 |     \sa QPieSlice::hovered() | 
| 383 | */ | 
| 384 | /*! | 
| 385 |     \qmlsignal PieSeries::hovered(PieSlice slice, bool state) | 
| 386 |     This signal is emitted when a mouse is hovered over the slice specified by | 
| 387 |     \a slice. When the mouse moves over the slice, \a state turns \c true, and | 
| 388 |     when the mouse moves away again, it turns \c false. | 
| 389 |  | 
| 390 |     The corresponding signal handler is \c onHovered. | 
| 391 | */ | 
| 392 |  | 
| 393 | /*! | 
| 394 |     \qmlmethod PieSlice PieSeries::at(int index) | 
| 395 |     Returns the slice at the position specified by \a index. Returns null if the | 
| 396 |     index is not valid. | 
| 397 | */ | 
| 398 |  | 
| 399 | /*! | 
| 400 |     \qmlmethod PieSlice PieSeries::find(string label) | 
| 401 |     Returns the first slice that has the label \a label. Returns null if the label | 
| 402 |     is not found. | 
| 403 | */ | 
| 404 |  | 
| 405 | /*! | 
| 406 |     \qmlmethod PieSlice PieSeries::append(string label, real value) | 
| 407 |     Adds a new slice with the label \a label and the value \a value to the pie. | 
| 408 | */ | 
| 409 |  | 
| 410 | /*! | 
| 411 |     \qmlmethod bool PieSeries::remove(PieSlice slice) | 
| 412 |     Removes the slice specified by \a slice from the pie. Returns \c true if the | 
| 413 |     removal was successful, \c false otherwise. | 
| 414 | */ | 
| 415 |  | 
| 416 | /*! | 
| 417 |     \qmlmethod PieSeries::clear() | 
| 418 |     Removes all slices from the pie. | 
| 419 | */ | 
| 420 |  | 
| 421 | /*! | 
| 422 |     Constructs a series object that is a child of \a parent. | 
| 423 | */ | 
| 424 | QPieSeries::QPieSeries(QObject *parent) | 
| 425 |     : QAbstractSeries(*new QPieSeriesPrivate(this), parent) | 
| 426 | { | 
| 427 |     Q_D(QPieSeries); | 
| 428 |     QObject::connect(sender: this, SIGNAL(countChanged()), receiver: d, SIGNAL(countChanged())); | 
| 429 | } | 
| 430 |  | 
| 431 | /*! | 
| 432 |     Removes the pie series and its slices. | 
| 433 | */ | 
| 434 | QPieSeries::~QPieSeries() | 
| 435 | { | 
| 436 |     // NOTE: d_prt destroyed by QObject | 
| 437 |     clear(); | 
| 438 | } | 
| 439 |  | 
| 440 | /*! | 
| 441 |     \reimp | 
| 442 |  | 
| 443 |     Returns the type of the series. | 
| 444 | */ | 
| 445 | QAbstractSeries::SeriesType QPieSeries::type() const | 
| 446 | { | 
| 447 |     return QAbstractSeries::SeriesTypePie; | 
| 448 | } | 
| 449 |  | 
| 450 | /*! | 
| 451 |     Appends the slice specified by \a slice to the series. | 
| 452 |     Slice ownership is passed to the series. | 
| 453 |  | 
| 454 |     Returns \c true if appending succeeds. | 
| 455 | */ | 
| 456 | bool QPieSeries::append(QPieSlice *slice) | 
| 457 | { | 
| 458 |     return append(slices: QList<QPieSlice *>() << slice); | 
| 459 | } | 
| 460 |  | 
| 461 | /*! | 
| 462 |     Appends the array of slices specified by \a slices to the series. | 
| 463 |     Slice ownership is passed to the series. | 
| 464 |  | 
| 465 |     Returns \c true if appending succeeds. | 
| 466 | */ | 
| 467 | bool QPieSeries::append(const QList<QPieSlice *> &slices) | 
| 468 | { | 
| 469 |     Q_D(QPieSeries); | 
| 470 |  | 
| 471 |     if (slices.size() == 0) | 
| 472 |         return false; | 
| 473 |  | 
| 474 |     for (auto *s : slices) { | 
| 475 |         if (!s || d->m_slices.contains(t: s)) | 
| 476 |             return false; | 
| 477 |         if (s->series()) // already added to some series | 
| 478 |             return false; | 
| 479 |         if (!isValidValue(value: s->value())) | 
| 480 |             return false; | 
| 481 |     } | 
| 482 |  | 
| 483 |     for (auto *s : slices) { | 
| 484 |         s->setParent(this); | 
| 485 |         QPieSlicePrivate::fromSlice(slice: s)->m_series = this; | 
| 486 |         d->m_slices << s; | 
| 487 |     } | 
| 488 |  | 
| 489 |     d->updateDerivativeData(); | 
| 490 |  | 
| 491 |     for (auto *s : slices) { | 
| 492 |         connect(sender: s, SIGNAL(valueChanged()), receiver: d, SLOT(sliceValueChanged())); | 
| 493 |         connect(sender: s, SIGNAL(clicked()), receiver: d, SLOT(sliceClicked())); | 
| 494 |         connect(sender: s, SIGNAL(hovered(bool)), receiver: d, SLOT(sliceHovered(bool))); | 
| 495 |         connect(sender: s, SIGNAL(pressed()), receiver: d, SLOT(slicePressed())); | 
| 496 |         connect(sender: s, SIGNAL(released()), receiver: d, SLOT(sliceReleased())); | 
| 497 |         connect(sender: s, SIGNAL(doubleClicked()), receiver: d, SLOT(sliceDoubleClicked())); | 
| 498 |     } | 
| 499 |  | 
| 500 |     emit added(slices); | 
| 501 |     emit countChanged(); | 
| 502 |  | 
| 503 |     return true; | 
| 504 | } | 
| 505 |  | 
| 506 | /*! | 
| 507 |     Appends the slice specified by \a slice to the series and returns a reference to the series. | 
| 508 |     Slice ownership is passed to the series. | 
| 509 | */ | 
| 510 | QPieSeries &QPieSeries::operator << (QPieSlice *slice) | 
| 511 | { | 
| 512 |     append(slice); | 
| 513 |     return *this; | 
| 514 | } | 
| 515 |  | 
| 516 |  | 
| 517 | /*! | 
| 518 |     Appends a single slice with the specified \a value and \a label to the series. | 
| 519 |     Slice ownership is passed to the series. | 
| 520 |     Returns null if \a value is \c NaN, \c Inf, or \c -Inf and adds nothing to the | 
| 521 |     series. | 
| 522 | */ | 
| 523 | QPieSlice *QPieSeries::append(const QString &label, qreal value) | 
| 524 | { | 
| 525 |     if (isValidValue(value)) { | 
| 526 |         QPieSlice *slice = new QPieSlice(label, value); | 
| 527 |         append(slice); | 
| 528 |         return slice; | 
| 529 |     } else { | 
| 530 |         return 0; | 
| 531 |     } | 
| 532 | } | 
| 533 |  | 
| 534 | /*! | 
| 535 |     Inserts the slice specified by \a slice to the series before the slice at | 
| 536 |     the position specified by \a index. | 
| 537 |     Slice ownership is passed to the series. | 
| 538 |  | 
| 539 |     Returns \c true if inserting succeeds. | 
| 540 | */ | 
| 541 | bool QPieSeries::insert(int index, QPieSlice *slice) | 
| 542 | { | 
| 543 |     Q_D(QPieSeries); | 
| 544 |  | 
| 545 |     if (index < 0 || index > d->m_slices.size()) | 
| 546 |         return false; | 
| 547 |  | 
| 548 |     if (!slice || d->m_slices.contains(t: slice)) | 
| 549 |         return false; | 
| 550 |  | 
| 551 |     if (slice->series()) // already added to some series | 
| 552 |         return false; | 
| 553 |  | 
| 554 |     if (!isValidValue(value: slice->value())) | 
| 555 |         return false; | 
| 556 |  | 
| 557 |     slice->setParent(this); | 
| 558 |     QPieSlicePrivate::fromSlice(slice)->m_series = this; | 
| 559 |     d->m_slices.insert(i: index, t: slice); | 
| 560 |  | 
| 561 |     d->updateDerivativeData(); | 
| 562 |  | 
| 563 |     connect(sender: slice, SIGNAL(valueChanged()), receiver: d, SLOT(sliceValueChanged())); | 
| 564 |     connect(sender: slice, SIGNAL(clicked()), receiver: d, SLOT(sliceClicked())); | 
| 565 |     connect(sender: slice, SIGNAL(hovered(bool)), receiver: d, SLOT(sliceHovered(bool))); | 
| 566 |     connect(sender: slice, SIGNAL(pressed()), receiver: d, SLOT(slicePressed())); | 
| 567 |     connect(sender: slice, SIGNAL(released()), receiver: d, SLOT(sliceReleased())); | 
| 568 |     connect(sender: slice, SIGNAL(doubleClicked()), receiver: d, SLOT(sliceDoubleClicked())); | 
| 569 |  | 
| 570 |     emit added(slices: QList<QPieSlice *>() << slice); | 
| 571 |     emit countChanged(); | 
| 572 |  | 
| 573 |     return true; | 
| 574 | } | 
| 575 |  | 
| 576 | /*! | 
| 577 |     Removes a single slice, specified by \a slice, from the series and deletes it | 
| 578 |     permanently. | 
| 579 |  | 
| 580 |     The pointer cannot be referenced after this call. | 
| 581 |  | 
| 582 |     Returns \c true if the removal succeeds. | 
| 583 | */ | 
| 584 | bool QPieSeries::remove(QPieSlice *slice) | 
| 585 | { | 
| 586 |     Q_D(QPieSeries); | 
| 587 |  | 
| 588 |     if (!d->m_slices.removeOne(t: slice)) | 
| 589 |         return false; | 
| 590 |  | 
| 591 |     d->updateDerivativeData(); | 
| 592 |  | 
| 593 |     emit removed(slices: QList<QPieSlice *>() << slice); | 
| 594 |     emit countChanged(); | 
| 595 |  | 
| 596 |     delete slice; | 
| 597 |     slice = 0; | 
| 598 |  | 
| 599 |     return true; | 
| 600 | } | 
| 601 |  | 
| 602 | /*! | 
| 603 |     Takes a single slice, specified by \a slice, from the series. Does not delete | 
| 604 |     the slice object. | 
| 605 |  | 
| 606 |     \note The series remains the slice's parent object. You must set the | 
| 607 |     parent object to take full ownership. | 
| 608 |  | 
| 609 |     Returns \c true if the take operation was successful. | 
| 610 | */ | 
| 611 | bool QPieSeries::take(QPieSlice *slice) | 
| 612 | { | 
| 613 |     Q_D(QPieSeries); | 
| 614 |  | 
| 615 |     if (!d->m_slices.removeOne(t: slice)) | 
| 616 |         return false; | 
| 617 |  | 
| 618 |     QPieSlicePrivate::fromSlice(slice)->m_series = 0; | 
| 619 |     slice->disconnect(receiver: d); | 
| 620 |  | 
| 621 |     d->updateDerivativeData(); | 
| 622 |  | 
| 623 |     emit removed(slices: QList<QPieSlice *>() << slice); | 
| 624 |     emit countChanged(); | 
| 625 |  | 
| 626 |     return true; | 
| 627 | } | 
| 628 |  | 
| 629 | /*! | 
| 630 |     Clears all slices from the series. | 
| 631 | */ | 
| 632 | void QPieSeries::clear() | 
| 633 | { | 
| 634 |     Q_D(QPieSeries); | 
| 635 |     if (d->m_slices.size() == 0) | 
| 636 |         return; | 
| 637 |  | 
| 638 |     QList<QPieSlice *> slices = d->m_slices; | 
| 639 |     foreach (QPieSlice *s, d->m_slices) | 
| 640 |         d->m_slices.removeOne(t: s); | 
| 641 |  | 
| 642 |     d->updateDerivativeData(); | 
| 643 |  | 
| 644 |     emit removed(slices); | 
| 645 |     emit countChanged(); | 
| 646 |  | 
| 647 |     foreach (QPieSlice *s, slices) | 
| 648 |         delete s; | 
| 649 | } | 
| 650 |  | 
| 651 | /*! | 
| 652 |     Returns a list of slices that belong to this series. | 
| 653 | */ | 
| 654 | QList<QPieSlice *> QPieSeries::slices() const | 
| 655 | { | 
| 656 |     Q_D(const QPieSeries); | 
| 657 |     return d->m_slices; | 
| 658 | } | 
| 659 |  | 
| 660 | /*! | 
| 661 |     Returns the number of the slices in this series. | 
| 662 | */ | 
| 663 | int QPieSeries::count() const | 
| 664 | { | 
| 665 |     Q_D(const QPieSeries); | 
| 666 |     return d->m_slices.size(); | 
| 667 | } | 
| 668 |  | 
| 669 | /*! | 
| 670 |     Returns \c true if the series is empty. | 
| 671 | */ | 
| 672 | bool QPieSeries::isEmpty() const | 
| 673 | { | 
| 674 |     Q_D(const QPieSeries); | 
| 675 |     return d->m_slices.isEmpty(); | 
| 676 | } | 
| 677 |  | 
| 678 | /*! | 
| 679 |     Returns the sum of all slice values in this series. | 
| 680 |  | 
| 681 |     \sa QPieSlice::value(), QPieSlice::setValue(), QPieSlice::percentage() | 
| 682 | */ | 
| 683 | qreal QPieSeries::sum() const | 
| 684 | { | 
| 685 |     Q_D(const QPieSeries); | 
| 686 |     return d->m_sum; | 
| 687 | } | 
| 688 |  | 
| 689 | void QPieSeries::setHoleSize(qreal holeSize) | 
| 690 | { | 
| 691 |     Q_D(QPieSeries); | 
| 692 |     holeSize = qBound(min: (qreal)0.0, val: holeSize, max: (qreal)1.0); | 
| 693 |     d->setSizes(innerSize: holeSize, outerSize: qMax(a: d->m_pieRelativeSize, b: holeSize)); | 
| 694 | } | 
| 695 |  | 
| 696 | qreal QPieSeries::holeSize() const | 
| 697 | { | 
| 698 |     Q_D(const QPieSeries); | 
| 699 |     return d->m_holeRelativeSize; | 
| 700 | } | 
| 701 |  | 
| 702 | void QPieSeries::setHorizontalPosition(qreal relativePosition) | 
| 703 | { | 
| 704 |     Q_D(QPieSeries); | 
| 705 |  | 
| 706 |     if (relativePosition < 0.0) | 
| 707 |         relativePosition = 0.0; | 
| 708 |     if (relativePosition > 1.0) | 
| 709 |         relativePosition = 1.0; | 
| 710 |  | 
| 711 |     if (!qFuzzyCompare(p1: d->m_pieRelativeHorPos, p2: relativePosition)) { | 
| 712 |         d->m_pieRelativeHorPos = relativePosition; | 
| 713 |         emit d->horizontalPositionChanged(); | 
| 714 |     } | 
| 715 | } | 
| 716 |  | 
| 717 | qreal QPieSeries::horizontalPosition() const | 
| 718 | { | 
| 719 |     Q_D(const QPieSeries); | 
| 720 |     return d->m_pieRelativeHorPos; | 
| 721 | } | 
| 722 |  | 
| 723 | void QPieSeries::setVerticalPosition(qreal relativePosition) | 
| 724 | { | 
| 725 |     Q_D(QPieSeries); | 
| 726 |  | 
| 727 |     if (relativePosition < 0.0) | 
| 728 |         relativePosition = 0.0; | 
| 729 |     if (relativePosition > 1.0) | 
| 730 |         relativePosition = 1.0; | 
| 731 |  | 
| 732 |     if (!qFuzzyCompare(p1: d->m_pieRelativeVerPos, p2: relativePosition)) { | 
| 733 |         d->m_pieRelativeVerPos = relativePosition; | 
| 734 |         emit d->verticalPositionChanged(); | 
| 735 |     } | 
| 736 | } | 
| 737 |  | 
| 738 | qreal QPieSeries::verticalPosition() const | 
| 739 | { | 
| 740 |     Q_D(const QPieSeries); | 
| 741 |     return d->m_pieRelativeVerPos; | 
| 742 | } | 
| 743 |  | 
| 744 | void QPieSeries::setPieSize(qreal relativeSize) | 
| 745 | { | 
| 746 |     Q_D(QPieSeries); | 
| 747 |     relativeSize = qBound(min: (qreal)0.0, val: relativeSize, max: (qreal)1.0); | 
| 748 |     d->setSizes(innerSize: qMin(a: d->m_holeRelativeSize, b: relativeSize), outerSize: relativeSize); | 
| 749 |  | 
| 750 | } | 
| 751 |  | 
| 752 | qreal QPieSeries::pieSize() const | 
| 753 | { | 
| 754 |     Q_D(const QPieSeries); | 
| 755 |     return d->m_pieRelativeSize; | 
| 756 | } | 
| 757 |  | 
| 758 |  | 
| 759 | void QPieSeries::setPieStartAngle(qreal angle) | 
| 760 | { | 
| 761 |     Q_D(QPieSeries); | 
| 762 |     if (qFuzzyCompare(p1: d->m_pieStartAngle, p2: angle)) | 
| 763 |         return; | 
| 764 |     d->m_pieStartAngle = angle; | 
| 765 |     d->updateDerivativeData(); | 
| 766 |     emit d->pieStartAngleChanged(); | 
| 767 | } | 
| 768 |  | 
| 769 | qreal QPieSeries::pieStartAngle() const | 
| 770 | { | 
| 771 |     Q_D(const QPieSeries); | 
| 772 |     return d->m_pieStartAngle; | 
| 773 | } | 
| 774 |  | 
| 775 | /*! | 
| 776 |     Sets the end angle of the pie. | 
| 777 |  | 
| 778 |     A full pie is 360 degrees, where 0 degrees is at 12 a'clock. | 
| 779 |  | 
| 780 |     \a angle must be greater than the start angle. | 
| 781 |  | 
| 782 |     \sa pieEndAngle(), pieStartAngle(), setPieStartAngle() | 
| 783 | */ | 
| 784 | void QPieSeries::setPieEndAngle(qreal angle) | 
| 785 | { | 
| 786 |     Q_D(QPieSeries); | 
| 787 |     if (qFuzzyCompare(p1: d->m_pieEndAngle, p2: angle)) | 
| 788 |         return; | 
| 789 |     d->m_pieEndAngle = angle; | 
| 790 |     d->updateDerivativeData(); | 
| 791 |     emit d->pieEndAngleChanged(); | 
| 792 | } | 
| 793 |  | 
| 794 | /*! | 
| 795 |     Returns the end angle of the pie. | 
| 796 |  | 
| 797 |     A full pie is 360 degrees, where 0 degrees is at 12 a'clock. | 
| 798 |  | 
| 799 |     \sa setPieEndAngle(), pieStartAngle(), setPieStartAngle() | 
| 800 | */ | 
| 801 | qreal QPieSeries::pieEndAngle() const | 
| 802 | { | 
| 803 |     Q_D(const QPieSeries); | 
| 804 |     return d->m_pieEndAngle; | 
| 805 | } | 
| 806 |  | 
| 807 | /*! | 
| 808 |     Sets the visibility of all slice labels to \a visible. | 
| 809 |  | 
| 810 |     \note This function affects only the current slices in the series. | 
| 811 |     If a new slice is added, the default label visibility is \c false. | 
| 812 |  | 
| 813 |     \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible() | 
| 814 | */ | 
| 815 | void QPieSeries::setLabelsVisible(bool visible) | 
| 816 | { | 
| 817 |     Q_D(QPieSeries); | 
| 818 |     foreach (QPieSlice *s, d->m_slices) | 
| 819 |         s->setLabelVisible(visible); | 
| 820 | } | 
| 821 |  | 
| 822 | /*! | 
| 823 |     Sets the position of all the slice labels to \a position. | 
| 824 |  | 
| 825 |     \note This function affects only the current slices in the series. | 
| 826 |     If a new slice is added, the default label position is QPieSlice::LabelOutside. | 
| 827 |  | 
| 828 |     \sa QPieSlice::labelPosition(), QPieSlice::setLabelPosition() | 
| 829 | */ | 
| 830 | void QPieSeries::setLabelsPosition(QPieSlice::LabelPosition position) | 
| 831 | { | 
| 832 |     Q_D(QPieSeries); | 
| 833 |     foreach (QPieSlice *s, d->m_slices) | 
| 834 |         s->setLabelPosition(position); | 
| 835 | } | 
| 836 |  | 
| 837 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
| 838 |  | 
| 839 |  | 
| 840 | QPieSeriesPrivate::QPieSeriesPrivate(QPieSeries *parent) : | 
| 841 |     QAbstractSeriesPrivate(parent), | 
| 842 |     m_pieRelativeHorPos(0.5), | 
| 843 |     m_pieRelativeVerPos(0.5), | 
| 844 |     m_pieRelativeSize(0.7), | 
| 845 |     m_pieStartAngle(0), | 
| 846 |     m_pieEndAngle(360), | 
| 847 |     m_sum(0), | 
| 848 |     m_holeRelativeSize(0.0) | 
| 849 | { | 
| 850 | } | 
| 851 |  | 
| 852 | QPieSeriesPrivate::~QPieSeriesPrivate() | 
| 853 | { | 
| 854 | } | 
| 855 |  | 
| 856 | void QPieSeriesPrivate::updateDerivativeData() | 
| 857 | { | 
| 858 |     // calculate sum of all slices | 
| 859 |     qreal sum = 0; | 
| 860 |     foreach (QPieSlice *s, m_slices) | 
| 861 |         sum += s->value(); | 
| 862 |  | 
| 863 |     if (!qFuzzyCompare(p1: m_sum, p2: sum)) { | 
| 864 |         m_sum = sum; | 
| 865 |         emit q_func()->sumChanged(); | 
| 866 |     } | 
| 867 |  | 
| 868 |     // nothing to show.. | 
| 869 |     if (qFuzzyCompare(p1: m_sum, p2: 0)) | 
| 870 |         return; | 
| 871 |  | 
| 872 |     // update slice attributes | 
| 873 |     qreal sliceAngle = m_pieStartAngle; | 
| 874 |     qreal pieSpan = m_pieEndAngle - m_pieStartAngle; | 
| 875 |     QList<QPieSlice *> changed; | 
| 876 |     foreach (QPieSlice *s, m_slices) { | 
| 877 |         QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(slice: s); | 
| 878 |         d->setPercentage(s->value() / m_sum); | 
| 879 |         d->setStartAngle(sliceAngle); | 
| 880 |         d->setAngleSpan(pieSpan * s->percentage()); | 
| 881 |         sliceAngle += s->angleSpan(); | 
| 882 |     } | 
| 883 |  | 
| 884 |  | 
| 885 |     emit calculatedDataChanged(); | 
| 886 | } | 
| 887 |  | 
| 888 | void QPieSeriesPrivate::setSizes(qreal innerSize, qreal outerSize) | 
| 889 | { | 
| 890 |     bool changed = false; | 
| 891 |  | 
| 892 |     if (!qFuzzyCompare(p1: m_holeRelativeSize, p2: innerSize)) { | 
| 893 |         m_holeRelativeSize = innerSize; | 
| 894 |         changed = true; | 
| 895 |     } | 
| 896 |  | 
| 897 |     if (!qFuzzyCompare(p1: m_pieRelativeSize, p2: outerSize)) { | 
| 898 |         m_pieRelativeSize = outerSize; | 
| 899 |         changed = true; | 
| 900 |     } | 
| 901 |  | 
| 902 |     if (changed) | 
| 903 |         emit pieSizeChanged(); | 
| 904 | } | 
| 905 |  | 
| 906 | QPieSeriesPrivate *QPieSeriesPrivate::fromSeries(QPieSeries *series) | 
| 907 | { | 
| 908 |     return series->d_func(); | 
| 909 | } | 
| 910 |  | 
| 911 | void QPieSeriesPrivate::sliceValueChanged() | 
| 912 | { | 
| 913 |     Q_ASSERT(m_slices.contains(qobject_cast<QPieSlice *>(sender()))); | 
| 914 |     updateDerivativeData(); | 
| 915 | } | 
| 916 |  | 
| 917 | void QPieSeriesPrivate::sliceClicked() | 
| 918 | { | 
| 919 |     QPieSlice *slice = qobject_cast<QPieSlice *>(object: sender()); | 
| 920 |     Q_ASSERT(m_slices.contains(slice)); | 
| 921 |     Q_Q(QPieSeries); | 
| 922 |     emit q->clicked(slice); | 
| 923 | } | 
| 924 |  | 
| 925 | void QPieSeriesPrivate::sliceHovered(bool state) | 
| 926 | { | 
| 927 |     QPieSlice *slice = qobject_cast<QPieSlice *>(object: sender()); | 
| 928 |     if (!m_slices.isEmpty()) { | 
| 929 |         Q_ASSERT(m_slices.contains(slice)); | 
| 930 |         Q_Q(QPieSeries); | 
| 931 |         emit q->hovered(slice, state); | 
| 932 |     } | 
| 933 | } | 
| 934 |  | 
| 935 | void QPieSeriesPrivate::slicePressed() | 
| 936 | { | 
| 937 |     QPieSlice *slice = qobject_cast<QPieSlice *>(object: sender()); | 
| 938 |     Q_ASSERT(m_slices.contains(slice)); | 
| 939 |     Q_Q(QPieSeries); | 
| 940 |     emit q->pressed(slice); | 
| 941 | } | 
| 942 |  | 
| 943 | void QPieSeriesPrivate::sliceReleased() | 
| 944 | { | 
| 945 |     QPieSlice *slice = qobject_cast<QPieSlice *>(object: sender()); | 
| 946 |     Q_ASSERT(m_slices.contains(slice)); | 
| 947 |     Q_Q(QPieSeries); | 
| 948 |     emit q->released(slice); | 
| 949 | } | 
| 950 |  | 
| 951 | void QPieSeriesPrivate::sliceDoubleClicked() | 
| 952 | { | 
| 953 |     QPieSlice *slice = qobject_cast<QPieSlice *>(object: sender()); | 
| 954 |     Q_ASSERT(m_slices.contains(slice)); | 
| 955 |     Q_Q(QPieSeries); | 
| 956 |     emit q->doubleClicked(slice); | 
| 957 | } | 
| 958 |  | 
| 959 | void QPieSeriesPrivate::initializeDomain() | 
| 960 | { | 
| 961 |     // does not apply to pie | 
| 962 | } | 
| 963 |  | 
| 964 | void QPieSeriesPrivate::initializeGraphics(QGraphicsItem* parent) | 
| 965 | { | 
| 966 |     Q_Q(QPieSeries); | 
| 967 |     PieChartItem *pie = new PieChartItem(q,parent); | 
| 968 |     m_item.reset(p: pie); | 
| 969 |     QAbstractSeriesPrivate::initializeGraphics(parent); | 
| 970 | } | 
| 971 |  | 
| 972 | void QPieSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, | 
| 973 |                                              int duration, QEasingCurve &curve) | 
| 974 | { | 
| 975 |     PieChartItem *item = static_cast<PieChartItem *>(m_item.get()); | 
| 976 |     Q_ASSERT(item); | 
| 977 |     if (item->animation()) | 
| 978 |         item->animation()->stopAndDestroyLater(); | 
| 979 |  | 
| 980 |     if (options.testFlag(flag: QChart::SeriesAnimations)) | 
| 981 |         item->setAnimation(new PieAnimation(item, duration, curve)); | 
| 982 |     else | 
| 983 |         item->setAnimation(0); | 
| 984 |     QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); | 
| 985 | } | 
| 986 |  | 
| 987 | QList<QLegendMarker*> QPieSeriesPrivate::createLegendMarkers(QLegend* legend) | 
| 988 | { | 
| 989 |     Q_Q(QPieSeries); | 
| 990 |     QList<QLegendMarker*> markers; | 
| 991 |     const auto slices = q->slices(); | 
| 992 |     for (QPieSlice *slice : slices) { | 
| 993 |         QPieLegendMarker* marker = new QPieLegendMarker(q,slice,legend); | 
| 994 |         markers << marker; | 
| 995 |     } | 
| 996 |     return markers; | 
| 997 | } | 
| 998 |  | 
| 999 | void QPieSeriesPrivate::initializeAxes() | 
| 1000 | { | 
| 1001 |  | 
| 1002 | } | 
| 1003 |  | 
| 1004 | QAbstractAxis::AxisType QPieSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const | 
| 1005 | { | 
| 1006 |     Q_UNUSED(orientation); | 
| 1007 |     return QAbstractAxis::AxisTypeNoAxis; | 
| 1008 | } | 
| 1009 |  | 
| 1010 | QAbstractAxis* QPieSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const | 
| 1011 | { | 
| 1012 |     Q_UNUSED(orientation); | 
| 1013 |     return 0; | 
| 1014 | } | 
| 1015 |  | 
| 1016 | void QPieSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced) | 
| 1017 | { | 
| 1018 |     //Q_Q(QPieSeries); | 
| 1019 |     //const QList<QColor>& colors = theme->seriesColors(); | 
| 1020 |     const QList<QGradient>& gradients = theme->seriesGradients(); | 
| 1021 |  | 
| 1022 |     for (int i(0); i < m_slices.size(); i++) { | 
| 1023 |  | 
| 1024 |         QColor penColor = ChartThemeManager::colorAt(gradient: gradients.at(i: index % gradients.size()), pos: 0.0); | 
| 1025 |  | 
| 1026 |         // Get color for a slice from a gradient linearly, beginning from the start of the gradient | 
| 1027 |         qreal pos = (qreal)(i + 1) / (qreal) m_slices.size(); | 
| 1028 |         QColor brushColor = ChartThemeManager::colorAt(gradient: gradients.at(i: index % gradients.size()), pos); | 
| 1029 |  | 
| 1030 |         QPieSlice *s = m_slices.at(i); | 
| 1031 |         QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(slice: s); | 
| 1032 |  | 
| 1033 |         if (forced || d->m_data.m_slicePen.isThemed()) | 
| 1034 |             d->setPen(pen: penColor, themed: true); | 
| 1035 |  | 
| 1036 |         if (forced || d->m_data.m_sliceBrush.isThemed()) | 
| 1037 |             d->setBrush(brush: brushColor, themed: true); | 
| 1038 |  | 
| 1039 |         if (forced || d->m_data.m_labelBrush.isThemed()) | 
| 1040 |             d->setLabelBrush(brush: theme->labelBrush().color(), themed: true); | 
| 1041 |  | 
| 1042 |         if (forced || d->m_data.m_labelFont.isThemed()) | 
| 1043 |             d->setLabelFont(font: theme->labelFont(), themed: true); | 
| 1044 |     } | 
| 1045 | } | 
| 1046 |  | 
| 1047 | QT_END_NAMESPACE | 
| 1048 |  | 
| 1049 | #include "moc_qpieseries.cpp" | 
| 1050 | #include "moc_qpieseries_p.cpp" | 
| 1051 |  |