1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtCharts/QPieModelMapper>
31#include <private/qpiemodelmapper_p.h>
32#include <QtCharts/QPieSeries>
33#include <QtCharts/QPieSlice>
34#include <QtCore/QAbstractItemModel>
35
36QT_CHARTS_BEGIN_NAMESPACE
37
38QPieModelMapper::QPieModelMapper(QObject *parent)
39 : QObject(parent),
40 d_ptr(new QPieModelMapperPrivate(this))
41{
42}
43
44QAbstractItemModel *QPieModelMapper::model() const
45{
46 Q_D(const QPieModelMapper);
47 return d->m_model;
48}
49
50void QPieModelMapper::setModel(QAbstractItemModel *model)
51{
52 if (model == 0)
53 return;
54
55 Q_D(QPieModelMapper);
56 if (d->m_model) {
57 disconnect(sender: d->m_model, signal: 0, receiver: d, member: 0);
58 }
59
60 d->m_model = model;
61 d->initializePieFromModel();
62 // connect signals from the model
63 connect(sender: d->m_model, SIGNAL(modelReset()), receiver: d, SLOT(initializePieFromModel()));
64 connect(sender: d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), receiver: d, SLOT(modelUpdated(QModelIndex,QModelIndex)));
65 connect(sender: d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), receiver: d, SLOT(modelRowsAdded(QModelIndex,int,int)));
66 connect(sender: d->m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), receiver: d, SLOT(modelRowsRemoved(QModelIndex,int,int)));
67 connect(sender: d->m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), receiver: d, SLOT(modelColumnsAdded(QModelIndex,int,int)));
68 connect(sender: d->m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), receiver: d, SLOT(modelColumnsRemoved(QModelIndex,int,int)));
69 connect(sender: d->m_model, SIGNAL(destroyed()), receiver: d, SLOT(handleModelDestroyed()));
70}
71
72QPieSeries *QPieModelMapper::series() const
73{
74 Q_D(const QPieModelMapper);
75 return d->m_series;
76}
77
78void QPieModelMapper::setSeries(QPieSeries *series)
79{
80 Q_D(QPieModelMapper);
81 if (d->m_series) {
82 disconnect(sender: d->m_series, signal: 0, receiver: d, member: 0);
83 }
84
85 if (series == 0)
86 return;
87
88 d->m_series = series;
89 d->initializePieFromModel();
90 // connect the signals from the series
91 connect(sender: d->m_series, SIGNAL(added(QList<QPieSlice*>)), receiver: d, SLOT(slicesAdded(QList<QPieSlice*>)));
92 connect(sender: d->m_series, SIGNAL(removed(QList<QPieSlice*>)), receiver: d, SLOT(slicesRemoved(QList<QPieSlice*>)));
93 connect(sender: d->m_series, SIGNAL(destroyed()), receiver: d, SLOT(handleSeriesDestroyed()));
94}
95
96/*!
97 Defines which row/column of the model contains the first slice value.
98 Minimal and default value is: 0
99*/
100int QPieModelMapper::first() const
101{
102 Q_D(const QPieModelMapper);
103 return d->m_first;
104}
105
106/*!
107 Sets which row/column of the model contains the \a first slice value.
108 Minimal and default value is: 0
109*/
110void QPieModelMapper::setFirst(int first)
111{
112 Q_D(QPieModelMapper);
113 d->m_first = qMax(a: first, b: 0);
114 d->initializePieFromModel();
115}
116
117/*!
118 Defines the number of rows/columns of the model that are mapped as the data for QPieSeries
119 Minimal and default value is: -1 (count limited by the number of rows/columns in the model)
120*/
121int QPieModelMapper::count() const
122{
123 Q_D(const QPieModelMapper);
124 return d->m_count;
125}
126
127/*!
128 Defines the \a count of rows/columns of the model that are mapped as the data for QPieSeries
129 Minimal and default value is: -1 (count limited by the number of rows/columns in the model)
130*/
131void QPieModelMapper::setCount(int count)
132{
133 Q_D(QPieModelMapper);
134 d->m_count = qMax(a: count, b: -1);
135 d->initializePieFromModel();
136}
137
138/*!
139 Returns the orientation that is used when QPieModelMapper accesses the model.
140 This mean whether the consecutive values/labels of the pie are read from row (Qt::Horizontal)
141 or from columns (Qt::Vertical)
142*/
143Qt::Orientation QPieModelMapper::orientation() const
144{
145 Q_D(const QPieModelMapper);
146 return d->m_orientation;
147}
148
149/*!
150 Returns the \a orientation that is used when QPieModelMapper accesses the model.
151 This mean whether the consecutive values/labels of the pie are read from row (Qt::Horizontal)
152 or from columns (Qt::Vertical)
153*/
154void QPieModelMapper::setOrientation(Qt::Orientation orientation)
155{
156 Q_D(QPieModelMapper);
157 d->m_orientation = orientation;
158 d->initializePieFromModel();
159}
160
161/*!
162 Returns which section of the model is kept in sync with the values of the pie's slices
163*/
164int QPieModelMapper::valuesSection() const
165{
166 Q_D(const QPieModelMapper);
167 return d->m_valuesSection;
168}
169
170/*!
171 Sets the model section that is kept in sync with the pie slices values.
172 Parameter \a valuesSection specifies the section of the model.
173*/
174void QPieModelMapper::setValuesSection(int valuesSection)
175{
176 Q_D(QPieModelMapper);
177 d->m_valuesSection = qMax(a: -1, b: valuesSection);
178 d->initializePieFromModel();
179}
180
181/*!
182 Returns which section of the model is kept in sync with the labels of the pie's slices
183*/
184int QPieModelMapper::labelsSection() const
185{
186 Q_D(const QPieModelMapper);
187 return d->m_labelsSection;
188}
189
190/*!
191 Sets the model section that is kept in sync with the pie slices labels.
192 Parameter \a labelsSection specifies the section of the model.
193*/
194void QPieModelMapper::setLabelsSection(int labelsSection)
195{
196 Q_D(QPieModelMapper);
197 d->m_labelsSection = qMax(a: -1, b: labelsSection);
198 d->initializePieFromModel();
199}
200
201///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
202
203QPieModelMapperPrivate::QPieModelMapperPrivate(QPieModelMapper *q) :
204 QObject(q),
205 m_series(0),
206 m_model(0),
207 m_first(0),
208 m_count(-1),
209 m_orientation(Qt::Vertical),
210 m_valuesSection(-1),
211 m_labelsSection(-1),
212 m_seriesSignalsBlock(false),
213 m_modelSignalsBlock(false),
214 q_ptr(q)
215{
216}
217
218void QPieModelMapperPrivate::blockModelSignals(bool block)
219{
220 m_modelSignalsBlock = block;
221}
222
223void QPieModelMapperPrivate::blockSeriesSignals(bool block)
224{
225 m_seriesSignalsBlock = block;
226}
227
228
229QPieSlice *QPieModelMapperPrivate::pieSlice(QModelIndex index) const
230{
231 if (!index.isValid())
232 return 0; // index is invalid
233
234 if (m_orientation == Qt::Vertical && (index.column() == m_valuesSection || index.column() == m_labelsSection)) {
235 if (index.row() >= m_first && (m_count == - 1 || index.row() < m_first + m_count)) {
236 if (m_model->index(row: index.row(), column: m_valuesSection).isValid() && m_model->index(row: index.row(), column: m_labelsSection).isValid())
237 return m_series->slices().at(i: index.row() - m_first);
238 else
239 return 0;
240 }
241 } else if (m_orientation == Qt::Horizontal && (index.row() == m_valuesSection || index.row() == m_labelsSection)) {
242 if (index.column() >= m_first && (m_count == - 1 || index.column() < m_first + m_count)) {
243 if (m_model->index(row: m_valuesSection, column: index.column()).isValid() && m_model->index(row: m_labelsSection, column: index.column()).isValid())
244 return m_series->slices().at(i: index.column() - m_first);
245 else
246 return 0;
247 }
248 }
249 return 0; // This part of model has not been mapped to any slice
250}
251
252QModelIndex QPieModelMapperPrivate::valueModelIndex(int slicePos)
253{
254 if (m_count != -1 && slicePos >= m_count)
255 return QModelIndex(); // invalid
256
257 if (m_orientation == Qt::Vertical)
258 return m_model->index(row: slicePos + m_first, column: m_valuesSection);
259 else
260 return m_model->index(row: m_valuesSection, column: slicePos + m_first);
261}
262
263QModelIndex QPieModelMapperPrivate::labelModelIndex(int slicePos)
264{
265 if (m_count != -1 && slicePos >= m_count)
266 return QModelIndex(); // invalid
267
268 if (m_orientation == Qt::Vertical)
269 return m_model->index(row: slicePos + m_first, column: m_labelsSection);
270 else
271 return m_model->index(row: m_labelsSection, column: slicePos + m_first);
272}
273
274bool QPieModelMapperPrivate::isLabelIndex(QModelIndex index) const
275{
276 if (m_orientation == Qt::Vertical && index.column() == m_labelsSection)
277 return true;
278 else if (m_orientation == Qt::Horizontal && index.row() == m_labelsSection)
279 return true;
280
281 return false;
282}
283
284bool QPieModelMapperPrivate::isValueIndex(QModelIndex index) const
285{
286 if (m_orientation == Qt::Vertical && index.column() == m_valuesSection)
287 return true;
288 else if (m_orientation == Qt::Horizontal && index.row() == m_valuesSection)
289 return true;
290
291 return false;
292}
293
294void QPieModelMapperPrivate::slicesAdded(QList<QPieSlice *> slices)
295{
296 if (m_seriesSignalsBlock)
297 return;
298
299 if (slices.count() == 0)
300 return;
301
302 int firstIndex = m_series->slices().indexOf(t: slices.at(i: 0));
303 if (firstIndex == -1)
304 return;
305
306 if (m_count != -1)
307 m_count += slices.count();
308
309 for (int i = firstIndex; i < firstIndex + slices.count(); i++) {
310 m_slices.insert(i, t: slices.at(i: i - firstIndex));
311 connect(sender: slices.at(i: i - firstIndex), SIGNAL(labelChanged()), receiver: this, SLOT(sliceLabelChanged()));
312 connect(sender: slices.at(i: i - firstIndex), SIGNAL(valueChanged()), receiver: this, SLOT(sliceValueChanged()));
313 }
314
315 blockModelSignals();
316 if (m_orientation == Qt::Vertical)
317 m_model->insertRows(row: firstIndex + m_first, count: slices.count());
318 else
319 m_model->insertColumns(column: firstIndex + m_first, count: slices.count());
320
321 for (int i = firstIndex; i < firstIndex + slices.count(); i++) {
322 m_model->setData(index: valueModelIndex(slicePos: i), value: slices.at(i: i - firstIndex)->value());
323 m_model->setData(index: labelModelIndex(slicePos: i), value: slices.at(i: i - firstIndex)->label());
324 }
325 blockModelSignals(block: false);
326}
327
328void QPieModelMapperPrivate::slicesRemoved(QList<QPieSlice *> slices)
329{
330 if (m_seriesSignalsBlock)
331 return;
332
333 if (slices.count() == 0)
334 return;
335
336 int firstIndex = m_slices.indexOf(t: slices.at(i: 0));
337 if (firstIndex == -1)
338 return;
339
340 if (m_count != -1)
341 m_count -= slices.count();
342
343 for (int i = firstIndex + slices.count() - 1; i >= firstIndex; i--)
344 m_slices.removeAt(i);
345
346 blockModelSignals();
347 if (m_orientation == Qt::Vertical)
348 m_model->removeRows(row: firstIndex + m_first, count: slices.count());
349 else
350 m_model->removeColumns(column: firstIndex + m_first, count: slices.count());
351 blockModelSignals(block: false);
352}
353
354void QPieModelMapperPrivate::sliceLabelChanged()
355{
356 if (m_seriesSignalsBlock)
357 return;
358
359 blockModelSignals();
360 QPieSlice *slice = qobject_cast<QPieSlice *>(object: QObject::sender());
361 m_model->setData(index: labelModelIndex(slicePos: m_series->slices().indexOf(t: slice)), value: slice->label());
362 blockModelSignals(block: false);
363}
364
365void QPieModelMapperPrivate::sliceValueChanged()
366{
367 if (m_seriesSignalsBlock)
368 return;
369
370 blockModelSignals();
371 QPieSlice *slice = qobject_cast<QPieSlice *>(object: QObject::sender());
372 m_model->setData(index: valueModelIndex(slicePos: m_series->slices().indexOf(t: slice)), value: slice->value());
373 blockModelSignals(block: false);
374}
375
376void QPieModelMapperPrivate::handleSeriesDestroyed()
377{
378 m_series = 0;
379}
380
381void QPieModelMapperPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
382{
383 if (m_model == 0 || m_series == 0)
384 return;
385
386 if (m_modelSignalsBlock)
387 return;
388
389 blockSeriesSignals();
390 QModelIndex index;
391 QPieSlice *slice;
392 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
393 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
394 index = topLeft.sibling(arow: row, acolumn: column);
395 slice = pieSlice(index);
396 if (slice) {
397 if (isValueIndex(index))
398 slice->setValue(m_model->data(index, role: Qt::DisplayRole).toReal());
399 if (isLabelIndex(index))
400 slice->setLabel(m_model->data(index, role: Qt::DisplayRole).toString());
401 }
402 }
403 }
404 blockSeriesSignals(block: false);
405}
406
407
408void QPieModelMapperPrivate::modelRowsAdded(QModelIndex parent, int start, int end)
409{
410 Q_UNUSED(parent);
411 if (m_modelSignalsBlock)
412 return;
413
414 blockSeriesSignals();
415 if (m_orientation == Qt::Vertical)
416 insertData(start, end);
417 else if (start <= m_valuesSection || start <= m_labelsSection) // if the changes affect the map - reinitialize the pie
418 initializePieFromModel();
419 blockSeriesSignals(block: false);
420}
421
422void QPieModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end)
423{
424 Q_UNUSED(parent);
425 if (m_modelSignalsBlock)
426 return;
427
428 blockSeriesSignals();
429 if (m_orientation == Qt::Vertical)
430 removeData(start, end);
431 else if (start <= m_valuesSection || start <= m_labelsSection) // if the changes affect the map - reinitialize the pie
432 initializePieFromModel();
433 blockSeriesSignals(block: false);
434}
435
436void QPieModelMapperPrivate::modelColumnsAdded(QModelIndex parent, int start, int end)
437{
438 Q_UNUSED(parent);
439 if (m_modelSignalsBlock)
440 return;
441
442 blockSeriesSignals();
443 if (m_orientation == Qt::Horizontal)
444 insertData(start, end);
445 else if (start <= m_valuesSection || start <= m_labelsSection) // if the changes affect the map - reinitialize the pie
446 initializePieFromModel();
447 blockSeriesSignals(block: false);
448}
449
450void QPieModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end)
451{
452 Q_UNUSED(parent);
453 if (m_modelSignalsBlock)
454 return;
455
456 blockSeriesSignals();
457 if (m_orientation == Qt::Horizontal)
458 removeData(start, end);
459 else if (start <= m_valuesSection || start <= m_labelsSection) // if the changes affect the map - reinitialize the pie
460 initializePieFromModel();
461 blockSeriesSignals(block: false);
462}
463
464void QPieModelMapperPrivate::handleModelDestroyed()
465{
466 m_model = 0;
467}
468
469void QPieModelMapperPrivate::insertData(int start, int end)
470{
471 if (m_model == 0 || m_series == 0)
472 return;
473
474 if (m_count != -1 && start >= m_first + m_count) {
475 return;
476 } else {
477 int addedCount = end - start + 1;
478 if (m_count != -1 && addedCount > m_count)
479 addedCount = m_count;
480 int first = qMax(a: start, b: m_first);
481 int last = qMin(a: first + addedCount - 1, b: m_orientation == Qt::Vertical ? m_model->rowCount() - 1 : m_model->columnCount() - 1);
482 for (int i = first; i <= last; i++) {
483 QModelIndex valueIndex = valueModelIndex(slicePos: i - m_first);
484 QModelIndex labelIndex = labelModelIndex(slicePos: i - m_first);
485 if (valueIndex.isValid() && labelIndex.isValid()) {
486 QPieSlice *slice = new QPieSlice;
487 slice->setValue(m_model->data(index: valueIndex, role: Qt::DisplayRole).toDouble());
488 slice->setLabel(m_model->data(index: labelIndex, role: Qt::DisplayRole).toString());
489 connect(sender: slice, SIGNAL(labelChanged()), receiver: this, SLOT(sliceLabelChanged()));
490 connect(sender: slice, SIGNAL(valueChanged()), receiver: this, SLOT(sliceValueChanged()));
491 m_series->insert(index: i - m_first, slice);
492 m_slices.insert(i: i - m_first, t: slice);
493 }
494 }
495
496 // remove excess of slices (abouve m_count)
497 if (m_count != -1 && m_series->slices().size() > m_count)
498 for (int i = m_series->slices().size() - 1; i >= m_count; i--) {
499 m_series->remove(slice: m_series->slices().at(i));
500 m_slices.removeAt(i);
501 }
502 }
503}
504
505void QPieModelMapperPrivate::removeData(int start, int end)
506{
507 if (m_model == 0 || m_series == 0)
508 return;
509
510 int removedCount = end - start + 1;
511 if (m_count != -1 && start >= m_first + m_count) {
512 return;
513 } else {
514 int toRemove = qMin(a: m_series->slices().size(), b: removedCount); // first find how many items can actually be removed
515 int first = qMax(a: start, b: m_first); // get the index of the first item that will be removed.
516 int last = qMin(a: first + toRemove - 1, b: m_series->slices().size() + m_first - 1); // get the index of the last item that will be removed.
517 for (int i = last; i >= first; i--) {
518 m_series->remove(slice: m_series->slices().at(i: i - m_first));
519 m_slices.removeAt(i: i - m_first);
520 }
521
522 if (m_count != -1) {
523 int itemsAvailable; // check how many are available to be added
524 if (m_orientation == Qt::Vertical)
525 itemsAvailable = m_model->rowCount() - m_first - m_series->slices().size();
526 else
527 itemsAvailable = m_model->columnCount() - m_first - m_series->slices().size();
528 int toBeAdded = qMin(a: itemsAvailable, b: m_count - m_series->slices().size()); // add not more items than there is space left to be filled.
529 int currentSize = m_series->slices().size();
530 if (toBeAdded > 0)
531 for (int i = m_series->slices().size(); i < currentSize + toBeAdded; i++) {
532 QModelIndex valueIndex = valueModelIndex(slicePos: i - m_first);
533 QModelIndex labelIndex = labelModelIndex(slicePos: i - m_first);
534 if (valueIndex.isValid() && labelIndex.isValid()) {
535 QPieSlice *slice = new QPieSlice;
536 slice->setValue(m_model->data(index: valueIndex, role: Qt::DisplayRole).toDouble());
537 slice->setLabel(m_model->data(index: labelIndex, role: Qt::DisplayRole).toString());
538 m_series->insert(index: i, slice);
539 m_slices.insert(i, t: slice);
540 }
541 }
542 }
543 }
544}
545
546void QPieModelMapperPrivate::initializePieFromModel()
547{
548 if (m_model == 0 || m_series == 0)
549 return;
550
551 blockSeriesSignals();
552 // clear current content
553 m_series->clear();
554 m_slices.clear();
555
556 // create the initial slices set
557 int slicePos = 0;
558 QModelIndex valueIndex = valueModelIndex(slicePos);
559 QModelIndex labelIndex = labelModelIndex(slicePos);
560 while (valueIndex.isValid() && labelIndex.isValid()) {
561 QPieSlice *slice = new QPieSlice;
562 slice->setLabel(m_model->data(index: labelIndex, role: Qt::DisplayRole).toString());
563 slice->setValue(m_model->data(index: valueIndex, role: Qt::DisplayRole).toDouble());
564 connect(sender: slice, SIGNAL(labelChanged()), receiver: this, SLOT(sliceLabelChanged()));
565 connect(sender: slice, SIGNAL(valueChanged()), receiver: this, SLOT(sliceValueChanged()));
566 m_series->append(slice);
567 m_slices.append(t: slice);
568 slicePos++;
569 valueIndex = valueModelIndex(slicePos);
570 labelIndex = labelModelIndex(slicePos);
571 }
572 blockSeriesSignals(block: false);
573}
574
575QT_CHARTS_END_NAMESPACE
576
577#include "moc_qpiemodelmapper_p.cpp"
578#include "moc_qpiemodelmapper.cpp"
579

source code of qtcharts/src/charts/piechart/qpiemodelmapper.cpp