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/QBarModelMapper>
31#include <private/qbarmodelmapper_p.h>
32#include <QtCharts/QAbstractBarSeries>
33#include <QtCharts/QBarSet>
34#include <QtCharts/QChart>
35#include <QtCore/QAbstractItemModel>
36
37QT_CHARTS_BEGIN_NAMESPACE
38
39/*!
40 \class QBarModelMapper
41 \inmodule QtCharts
42 \brief The QBarModelMapper class is the base class for model mapper classes.
43 \internal
44
45 Model mappers enable using a data model derived from the QAbstractItemModel class
46 as a data source for a chart.
47*/
48
49QBarModelMapper::QBarModelMapper(QObject *parent) :
50 QObject(parent),
51 d_ptr(new QBarModelMapperPrivate(this))
52{
53}
54
55QAbstractItemModel *QBarModelMapper::model() const
56{
57 Q_D(const QBarModelMapper);
58 return d->m_model;
59}
60
61void QBarModelMapper::setModel(QAbstractItemModel *model)
62{
63 if (model == 0)
64 return;
65
66 Q_D(QBarModelMapper);
67 if (d->m_model)
68 disconnect(sender: d->m_model, signal: 0, receiver: d, member: 0);
69
70 d->m_model = model;
71 d->initializeBarFromModel();
72 // connect signals from the model
73 connect(sender: d->m_model, SIGNAL(modelReset()), receiver: d, SLOT(initializeBarFromModel()));
74 connect(sender: d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), receiver: d, SLOT(modelUpdated(QModelIndex,QModelIndex)));
75 connect(sender: d->m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), receiver: d, SLOT(modelHeaderDataUpdated(Qt::Orientation,int,int)));
76 connect(sender: d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), receiver: d, SLOT(modelRowsAdded(QModelIndex,int,int)));
77 connect(sender: d->m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), receiver: d, SLOT(modelRowsRemoved(QModelIndex,int,int)));
78 connect(sender: d->m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), receiver: d, SLOT(modelColumnsAdded(QModelIndex,int,int)));
79 connect(sender: d->m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), receiver: d, SLOT(modelColumnsRemoved(QModelIndex,int,int)));
80 connect(sender: d->m_model, SIGNAL(destroyed()), receiver: d, SLOT(handleModelDestroyed()));
81}
82
83QAbstractBarSeries *QBarModelMapper::series() const
84{
85 Q_D(const QBarModelMapper);
86 return d->m_series;
87}
88
89void QBarModelMapper::setSeries(QAbstractBarSeries *series)
90{
91 Q_D(QBarModelMapper);
92 if (d->m_series)
93 disconnect(sender: d->m_series, signal: 0, receiver: d, member: 0);
94
95 if (series == 0)
96 return;
97
98 d->m_series = series;
99 d->initializeBarFromModel();
100 // connect the signals from the series
101 connect(sender: d->m_series, SIGNAL(barsetsAdded(QList<QBarSet*>)), receiver: d, SLOT(barSetsAdded(QList<QBarSet*>)));
102 connect(sender: d->m_series, SIGNAL(barsetsRemoved(QList<QBarSet*>)), receiver: d, SLOT(barSetsRemoved(QList<QBarSet*>)));
103 connect(sender: d->m_series, SIGNAL(destroyed()), receiver: d, SLOT(handleSeriesDestroyed()));
104}
105
106/*!
107 Returns which row/column of the model contains the first values of the QBarSets in the series.
108 The default value is 0.
109*/
110int QBarModelMapper::first() const
111{
112 Q_D(const QBarModelMapper);
113 return d->m_first;
114}
115
116/*!
117 Sets which row of the model contains the \a first values of the QBarSets in the series.
118 The default value is 0.
119*/
120void QBarModelMapper::setFirst(int first)
121{
122 Q_D(QBarModelMapper);
123 d->m_first = qMax(a: first, b: 0);
124 d->initializeBarFromModel();
125}
126
127/*!
128 Returns the number of rows/columns of the model that are mapped as the data for QAbstractBarSeries
129 Minimal and default value is: -1 (count limited by the number of rows/columns in the model)
130*/
131int QBarModelMapper::count() const
132{
133 Q_D(const QBarModelMapper);
134 return d->m_count;
135}
136
137/*!
138 Sets the \a count of rows/columns of the model that are mapped as the data for QAbstractBarSeries
139 Minimal and default value is: -1 (count limited by the number of rows/columns in the model)
140*/
141void QBarModelMapper::setCount(int count)
142{
143 Q_D(QBarModelMapper);
144 d->m_count = qMax(a: count, b: -1);
145 d->initializeBarFromModel();
146}
147
148/*!
149 Returns the orientation that is used when QBarModelMapper accesses the model.
150 This mean whether the consecutive values of the bar set are read from row (Qt::Horizontal)
151 or from columns (Qt::Vertical)
152*/
153Qt::Orientation QBarModelMapper::orientation() const
154{
155 Q_D(const QBarModelMapper);
156 return d->m_orientation;
157}
158
159/*!
160 Returns the \a orientation that is used when QBarModelMapper accesses the model.
161 This mean whether the consecutive values of the pie are read from row (Qt::Horizontal)
162 or from columns (Qt::Vertical)
163*/
164void QBarModelMapper::setOrientation(Qt::Orientation orientation)
165{
166 Q_D(QBarModelMapper);
167 d->m_orientation = orientation;
168 d->initializeBarFromModel();
169}
170
171/*!
172 Returns which section of the model is used as the data source for the first bar set
173*/
174int QBarModelMapper::firstBarSetSection() const
175{
176 Q_D(const QBarModelMapper);
177 return d->m_firstBarSetSection;
178}
179
180/*!
181 Sets the model section that is used as the data source for the first bar set
182 Parameter \a firstBarSetSection specifies the section of the model.
183*/
184void QBarModelMapper::setFirstBarSetSection(int firstBarSetSection)
185{
186 Q_D(QBarModelMapper);
187 d->m_firstBarSetSection = qMax(a: -1, b: firstBarSetSection);
188 d->initializeBarFromModel();
189}
190
191/*!
192 Returns which section of the model is used as the data source for the last bar set
193*/
194int QBarModelMapper::lastBarSetSection() const
195{
196 Q_D(const QBarModelMapper);
197 return d->m_lastBarSetSection;
198}
199
200/*!
201 Sets the model section that is used as the data source for the last bar set
202 Parameter \a lastBarSetSection specifies the section of the model.
203*/
204void QBarModelMapper::setLastBarSetSection(int lastBarSetSection)
205{
206 Q_D(QBarModelMapper);
207 d->m_lastBarSetSection = qMax(a: -1, b: lastBarSetSection);
208 d->initializeBarFromModel();
209}
210
211///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
212
213QBarModelMapperPrivate::QBarModelMapperPrivate(QBarModelMapper *q) :
214 QObject(q),
215 m_series(0),
216 m_model(0),
217 m_first(0),
218 m_count(-1),
219 m_orientation(Qt::Vertical),
220 m_firstBarSetSection(-1),
221 m_lastBarSetSection(-1),
222 m_seriesSignalsBlock(false),
223 m_modelSignalsBlock(false),
224 q_ptr(q)
225{
226}
227
228void QBarModelMapperPrivate::blockModelSignals(bool block)
229{
230 m_modelSignalsBlock = block;
231}
232
233void QBarModelMapperPrivate::blockSeriesSignals(bool block)
234{
235 m_seriesSignalsBlock = block;
236}
237
238QBarSet *QBarModelMapperPrivate::barSet(QModelIndex index)
239{
240 if (!index.isValid())
241 return 0;
242
243 if (m_orientation == Qt::Vertical && index.column() >= m_firstBarSetSection && index.column() <= m_lastBarSetSection) {
244 if (index.row() >= m_first && (m_count == - 1 || index.row() < m_first + m_count)) {
245 return m_series->barSets().at(i: index.column() - m_firstBarSetSection);
246 }
247 } else if (m_orientation == Qt::Horizontal && index.row() >= m_firstBarSetSection && index.row() <= m_lastBarSetSection) {
248 if (index.column() >= m_first && (m_count == - 1 || index.column() < m_first + m_count))
249 return m_series->barSets().at(i: index.row() - m_firstBarSetSection);
250 }
251 return 0; // This part of model has not been mapped to any slice
252}
253
254QModelIndex QBarModelMapperPrivate::barModelIndex(int barSection, int posInBar)
255{
256 if (m_count != -1 && posInBar >= m_count)
257 return QModelIndex(); // invalid
258
259 if (barSection < m_firstBarSetSection || barSection > m_lastBarSetSection)
260 return QModelIndex(); // invalid
261
262 if (m_orientation == Qt::Vertical)
263 return m_model->index(row: posInBar + m_first, column: barSection);
264 else
265 return m_model->index(row: barSection, column: posInBar + m_first);
266}
267
268void QBarModelMapperPrivate::handleSeriesDestroyed()
269{
270 m_series = 0;
271}
272
273void QBarModelMapperPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
274{
275 Q_UNUSED(topLeft)
276 Q_UNUSED(bottomRight)
277
278 if (m_model == 0 || m_series == 0)
279 return;
280
281 if (m_modelSignalsBlock)
282 return;
283
284 blockSeriesSignals();
285 QModelIndex index;
286 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
287 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
288 index = topLeft.sibling(arow: row, acolumn: column);
289 QBarSet *bar = barSet(index);
290 if (bar) {
291 if (m_orientation == Qt::Vertical)
292 bar->replace(index: row - m_first, value: m_model->data(index).toReal());
293 else
294 bar->replace(index: column - m_first, value: m_model->data(index).toReal());
295 }
296 }
297 }
298 blockSeriesSignals(block: false);
299}
300
301void QBarModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first, int last)
302{
303 if (m_model == 0 || m_series == 0)
304 return;
305
306 if (m_modelSignalsBlock)
307 return;
308
309 blockSeriesSignals();
310 if (orientation != m_orientation) {
311 for (int section = first; section <= last; section++) {
312 if (section >= m_firstBarSetSection && section <= m_lastBarSetSection) {
313 QBarSet *bar = m_series->barSets().at(i: section - m_firstBarSetSection);
314 if (bar)
315 bar->setLabel(m_model->headerData(section, orientation).toString());
316 }
317 }
318 }
319 blockSeriesSignals(block: false);
320}
321
322void QBarModelMapperPrivate::modelRowsAdded(QModelIndex parent, int start, int end)
323{
324 Q_UNUSED(parent)
325 if (m_modelSignalsBlock)
326 return;
327
328 blockSeriesSignals();
329 if (m_orientation == Qt::Vertical)
330 insertData(start, end);
331 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
332 initializeBarFromModel();
333 blockSeriesSignals(block: false);
334}
335
336void QBarModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end)
337{
338 Q_UNUSED(parent)
339 if (m_modelSignalsBlock)
340 return;
341
342 blockSeriesSignals();
343 if (m_orientation == Qt::Vertical)
344 removeData(start, end);
345 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
346 initializeBarFromModel();
347 blockSeriesSignals(block: false);
348}
349
350void QBarModelMapperPrivate::modelColumnsAdded(QModelIndex parent, int start, int end)
351{
352 Q_UNUSED(parent)
353 if (m_modelSignalsBlock)
354 return;
355
356 blockSeriesSignals();
357 if (m_orientation == Qt::Horizontal)
358 insertData(start, end);
359 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
360 initializeBarFromModel();
361 blockSeriesSignals(block: false);
362}
363
364void QBarModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end)
365{
366 Q_UNUSED(parent)
367 if (m_modelSignalsBlock)
368 return;
369
370 blockSeriesSignals();
371 if (m_orientation == Qt::Horizontal)
372 removeData(start, end);
373 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
374 initializeBarFromModel();
375 blockSeriesSignals(block: false);
376}
377
378void QBarModelMapperPrivate::handleModelDestroyed()
379{
380 m_model = 0;
381}
382
383void QBarModelMapperPrivate::insertData(int start, int end)
384{
385 Q_UNUSED(end)
386 Q_UNUSED(start)
387 Q_UNUSED(end)
388 // Currently barchart needs to be fully recalculated when change is made.
389 // Re-initialize
390 initializeBarFromModel();
391}
392
393void QBarModelMapperPrivate::removeData(int start, int end)
394{
395 Q_UNUSED(end)
396 Q_UNUSED(start)
397 Q_UNUSED(end)
398 // Currently barchart needs to be fully recalculated when change is made.
399 // Re-initialize
400 initializeBarFromModel();
401}
402
403void QBarModelMapperPrivate::barSetsAdded(QList<QBarSet *> sets)
404{
405 if (m_seriesSignalsBlock)
406 return;
407
408 if (sets.count() == 0)
409 return;
410
411 int firstIndex = m_series->barSets().indexOf(t: sets.at(i: 0));
412 if (firstIndex == -1)
413 return;
414
415 int maxCount = 0;
416 for (int i = 0; i < sets.count(); i++) {
417 if (sets.at(i)->count() > m_count)
418 maxCount = sets.at(i)->count();
419 }
420
421 if (m_count != -1 && m_count < maxCount)
422 m_count = maxCount;
423
424 m_lastBarSetSection += sets.count();
425
426 blockModelSignals();
427 int modelCapacity = m_orientation == Qt::Vertical ? m_model->rowCount() - m_first : m_model->columnCount() - m_first;
428 if (maxCount > modelCapacity) {
429 if (m_orientation == Qt::Vertical)
430 m_model->insertRows(row: m_model->rowCount(), count: maxCount - modelCapacity);
431 else
432 m_model->insertColumns(column: m_model->columnCount(), count: maxCount - modelCapacity);
433 }
434
435 if (m_orientation == Qt::Vertical)
436 m_model->insertColumns(column: firstIndex + m_firstBarSetSection, count: sets.count());
437 else
438 m_model->insertRows(row: firstIndex + m_firstBarSetSection, count: sets.count());
439
440
441 for (int i = firstIndex + m_firstBarSetSection; i < firstIndex + m_firstBarSetSection + sets.count(); i++) {
442 m_model->setHeaderData(section: i, orientation: m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, value: sets.at(i: i - firstIndex - m_firstBarSetSection)->label());
443 for (int j = 0; j < sets.at(i: i - firstIndex - m_firstBarSetSection)->count(); j++)
444 m_model->setData(index: barModelIndex(barSection: i, posInBar: j), value: sets.at(i: i - firstIndex - m_firstBarSetSection)->at(index: j));
445 }
446 blockModelSignals(block: false);
447 initializeBarFromModel();
448}
449
450void QBarModelMapperPrivate::barSetsRemoved(QList<QBarSet *> sets)
451{
452 if (m_seriesSignalsBlock)
453 return;
454
455 if (sets.count() == 0)
456 return;
457
458 int firstIndex = m_barSets.indexOf(t: sets.at(i: 0));
459 if (firstIndex == -1)
460 return;
461
462 m_lastBarSetSection -= sets.count();
463
464 for (int i = firstIndex + sets.count() - 1; i >= firstIndex; i--)
465 m_barSets.removeAt(i);
466
467 blockModelSignals();
468 if (m_orientation == Qt::Vertical)
469 m_model->removeColumns(column: firstIndex + m_firstBarSetSection, count: sets.count());
470 else
471 m_model->removeRows(row: firstIndex + m_firstBarSetSection, count: sets.count());
472 blockModelSignals(block: false);
473 initializeBarFromModel();
474}
475
476void QBarModelMapperPrivate::valuesAdded(int index, int count)
477{
478 if (m_seriesSignalsBlock)
479 return;
480
481 if (m_count != -1)
482 m_count += count;
483
484 int barSetIndex = m_barSets.indexOf(t: qobject_cast<QBarSet *>(object: QObject::sender()));
485
486 blockModelSignals();
487 if (m_orientation == Qt::Vertical)
488 m_model->insertRows(row: index + m_first, count);
489 else
490 m_model->insertColumns(column: index + m_first, count);
491
492 for (int j = index; j < index + count; j++)
493 m_model->setData(index: barModelIndex(barSection: barSetIndex + m_firstBarSetSection, posInBar: j), value: m_barSets.at(i: barSetIndex)->at(index: j));
494
495 blockModelSignals(block: false);
496 initializeBarFromModel();
497}
498
499void QBarModelMapperPrivate::valuesRemoved(int index, int count)
500{
501 if (m_seriesSignalsBlock)
502 return;
503
504 if (m_count != -1)
505 m_count -= count;
506
507 blockModelSignals();
508 if (m_orientation == Qt::Vertical)
509 m_model->removeRows(row: index + m_first, count);
510 else
511 m_model->removeColumns(column: index + m_first, count);
512
513 blockModelSignals(block: false);
514 initializeBarFromModel();
515}
516
517void QBarModelMapperPrivate::barLabelChanged()
518{
519 if (m_seriesSignalsBlock)
520 return;
521
522 int barSetIndex = m_barSets.indexOf(t: qobject_cast<QBarSet *>(object: QObject::sender()));
523
524 blockModelSignals();
525 m_model->setHeaderData(section: barSetIndex + m_firstBarSetSection, orientation: m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, value: m_barSets.at(i: barSetIndex)->label());
526 blockModelSignals(block: false);
527 initializeBarFromModel();
528}
529
530void QBarModelMapperPrivate::barValueChanged(int index)
531{
532 if (m_seriesSignalsBlock)
533 return;
534
535 int barSetIndex = m_barSets.indexOf(t: qobject_cast<QBarSet *>(object: QObject::sender()));
536
537 blockModelSignals();
538 m_model->setData(index: barModelIndex(barSection: barSetIndex + m_firstBarSetSection, posInBar: index), value: m_barSets.at(i: barSetIndex)->at(index));
539 blockModelSignals(block: false);
540 initializeBarFromModel();
541}
542
543QBarSet *qt_allocate_bar_set_cpp(const QString &label)
544{
545 return new QBarSet(label);
546}
547
548Q_CHARTS_EXPORT QBarSet *(*qt_allocate_bar_set)(const QString &label) = &qt_allocate_bar_set_cpp;
549
550void QBarModelMapperPrivate::initializeBarFromModel()
551{
552 if (m_model == 0 || m_series == 0)
553 return;
554
555 blockSeriesSignals();
556 // clear current content
557 m_series->clear();
558 m_barSets.clear();
559
560 // create the initial bar sets
561 for (int i = m_firstBarSetSection; i <= m_lastBarSetSection; i++) {
562 int posInBar = 0;
563 QModelIndex barIndex = barModelIndex(barSection: i, posInBar);
564 // check if there is such model index
565 if (barIndex.isValid()) {
566 QBarSet *barSet = qt_allocate_bar_set(m_model->headerData(section: i, orientation: m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical).toString());
567 while (barIndex.isValid()) {
568 barSet->append(value: m_model->data(index: barIndex, role: Qt::DisplayRole).toDouble());
569 posInBar++;
570 barIndex = barModelIndex(barSection: i, posInBar);
571 }
572 connect(sender: barSet, SIGNAL(valuesAdded(int,int)), receiver: this, SLOT(valuesAdded(int,int)));
573 connect(sender: barSet, SIGNAL(valuesRemoved(int,int)), receiver: this, SLOT(valuesRemoved(int,int)));
574 connect(sender: barSet, SIGNAL(valueChanged(int)), receiver: this, SLOT(barValueChanged(int)));
575 connect(sender: barSet, SIGNAL(labelChanged()), receiver: this, SLOT(barLabelChanged()));
576 m_series->append(set: barSet);
577 m_barSets.append(t: barSet);
578 } else {
579 break;
580 }
581 }
582 blockSeriesSignals(block: false);
583}
584
585QT_CHARTS_END_NAMESPACE
586
587#include "moc_qbarmodelmapper.cpp"
588#include "moc_qbarmodelmapper_p.cpp"
589

source code of qtcharts/src/charts/barchart/qbarmodelmapper.cpp