1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3#include <QtCore/QAbstractItemModel>
4#include <QtGraphs/QXYModelMapper>
5#include <QtGraphs/QXYSeries>
6#include "qxymodelmapper_p.h"
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 \class QXYModelMapper
12 \inmodule QtGraphs
13 \ingroup graphs_2D
14
15 \brief The QXYModelMapper class is a model mapper for line,
16 spline, and scatter series.
17
18 Model mappers enable using a data model derived from the QAbstractItemModel
19 class as a data source for a graph. A model mapper is used to
20 create a connection between a line, spline, or scatter series.
21 A \e TableModel is a natural choice
22 for the model.
23
24 Both model and series properties can be used to manipulate the data. The
25 model mapper keeps the series and the data model in sync.
26
27 \sa QXYSeries
28*/
29/*!
30 \qmltype XYModelMapper
31 \nativetype QXYModelMapper
32 \inqmlmodule QtGraphs
33 \ingroup graphs_qml_2D
34
35 \brief A model mapper for XYSeries.
36
37 Model mappers enable using a data model derived from the QAbstractItemModel
38 class as a data source for a graph. A model mapper is used to
39 create a connection between a line, spline, or scatter series.
40 A \e TableModel is a natural choice
41 for the model.
42
43 Both model and series properties can be used to manipulate the data. The
44 model mapper keeps the series and the data model in sync.
45
46 \sa XYSeries
47*/
48
49/*!
50 \property QXYModelMapper::series
51 \brief The series that is used by the mapper.
52
53 All the data in the series is discarded when it is set to the mapper.
54 When a new series is specified, the old series is disconnected (but it
55 preserves its data).
56*/
57/*!
58 \qmlproperty XYSeries XYModelMapper::series
59 The series that is used by the mapper. All the data in the series is
60 discarded when it is set to the mapper. When a new series is specified, the
61 old series is disconnected (but it preserves its data).
62*/
63
64/*!
65 \property QXYModelMapper::model
66 \brief The model that is used by the mapper.
67*/
68/*!
69 \qmlproperty SomeModel XYModelMapper::model
70 The data model that is used by the mapper. You need to implement the model
71 and expose it to QML.
72
73 \note The model has to support adding and removing rows or columns and
74 modifying the data in the cells.
75*/
76
77/*!
78 \property QXYModelMapper::xSection
79 \brief The section of the model that contains the x-coordinates of data
80 points.
81
82 The default value is -1 (invalid mapping).
83
84 \sa QXYModelMapper::orientation
85*/
86/*!
87 \qmlproperty qsizetype XYModelMapper::xSection
88 the section of the model that contains the x-coordinates of data points.
89 The default value is -1 (invalid mapping).
90
91 \sa orientation
92*/
93
94/*!
95 \property QXYModelMapper::ySection
96 \brief the section of the model that contains the y-coordinates of data
97 points.
98
99 The default value is -1 (invalid mapping).
100
101 \sa QXYModelMapper::orientation
102*/
103/*!
104 \qmlproperty qsizetype XYModelMapper::ySection
105 the section of the model that contains the y-coordinates of data points.
106 The default value is -1 (invalid mapping).
107
108 \sa orientation
109*/
110
111/*!
112 \property QXYModelMapper::first
113 \brief The row of the model that contains the data for the first point
114 of the series.
115
116 The minimum and default value is 0.
117
118 \sa QXYModelMapper::orientation
119*/
120/*!
121 \qmlproperty qsizetype XYModelMapper::first
122 The row of the model that contains the data for the first point of the series.
123 The default value is 0.
124
125 \sa orientation
126*/
127
128/*!
129 \property QXYModelMapper::count
130 \brief The number of rows of the model that are mapped as the data for series.
131
132 The minimum and default value is -1 (the number is limited by the number of
133 rows in the model).
134
135 \sa QXYModelMapper::orientation
136*/
137/*!
138 \qmlproperty qsizetype XYModelMapper::count
139 The number of rows of the model that are mapped as the data for series. The default value is
140 -1 (the number is limited by the number of rows in the model).
141
142 \sa orientation
143*/
144
145/*!
146 \property QXYModelMapper::orientation
147 \brief Tells the modelmapper how to map data from a model. If
148 \c{Qt::Vertical} is used, each of the model's columns defines a bar set, and the
149 model's rows define the categories. When the orientation is set to
150 \c{Qt::Horizontal}, each of the model's rows defines a bar set, and the model's
151 columns define categories.
152
153 The default value is \c{Qt::Vertical}
154*/
155/*!
156 \qmlproperty orientation XYModelMapper::orientation
157 Tells the modelmapper how to map data from a model. If
158 \c{Qt.Vertical} is used, the model has \e X and \e Y columns, and the
159 model's rows define the data points. When the orientation is set to
160 \c{Qt.Horizontal}, the model has \e X and \e Y rows, and the model's
161 columns define the data points.
162*/
163
164/*!
165 \qmlsignal QXYModelMapper::seriesChanged()
166
167 This signal is emitted when the series that the mapper is connected to changes.
168*/
169
170/*!
171 \qmlsignal XYModelMapper::modelChanged()
172
173 This signal is emitted when the model that the mapper is connected to changes.
174*/
175
176/*!
177 \qmlsignal XYModelMapper::xSectionChanged()
178
179 This signal is emitted when the section that contains the x-coordinates of
180 data points changes.
181*/
182
183/*!
184 \qmlsignal XYModelMapper::ySectionChanged()
185
186 This signal is emitted when the section that contains the y-coordinates of
187 data points changes.
188*/
189
190/*!
191 \qmlsignal XYModelMapper::firstChanged()
192 This signal is emitted when the first row changes.
193*/
194
195/*!
196 \qmlsignal XYModelMapper::countChanged()
197 This signal is emitted when the number of rows changes.
198*/
199
200/*!
201 \qmlsignal BarModelMapper::orientationChanged()
202 This signal is emitted when the orientation changes.
203*/
204
205QXYModelMapper::~QXYModelMapper() {}
206
207QXYModelMapper::QXYModelMapper(QObject *parent)
208 : QObject{*(new QXYModelMapperPrivate), parent}
209{}
210
211QXYModelMapper::QXYModelMapper(QXYModelMapperPrivate &dd, QObject *parent)
212 : QObject(dd, parent)
213{}
214
215QAbstractItemModel *QXYModelMapper::model() const
216{
217 Q_D(const QXYModelMapper);
218 return d->m_model;
219}
220
221void QXYModelMapper::setModel(QAbstractItemModel *model)
222{
223 if (model == 0)
224 return;
225
226 Q_D(QXYModelMapper);
227 if (d->m_model) {
228 QObjectPrivate::disconnect(sender: d->m_model,
229 signal: &QAbstractItemModel::dataChanged,
230 receiverPrivate: d,
231 slot: &QXYModelMapperPrivate::onModelUpdated);
232 QObjectPrivate::disconnect(sender: d->m_model,
233 signal: &QAbstractItemModel::rowsInserted,
234 receiverPrivate: d,
235 slot: &QXYModelMapperPrivate::onModelRowsAdded);
236 QObjectPrivate::disconnect(sender: d->m_model,
237 signal: &QAbstractItemModel::rowsRemoved,
238 receiverPrivate: d,
239 slot: &QXYModelMapperPrivate::onModelRowsRemoved);
240 QObjectPrivate::disconnect(sender: d->m_model,
241 signal: &QAbstractItemModel::columnsInserted,
242 receiverPrivate: d,
243 slot: &QXYModelMapperPrivate::onModelColumnsAdded);
244 QObjectPrivate::disconnect(sender: d->m_model,
245 signal: &QAbstractItemModel::columnsRemoved,
246 receiverPrivate: d,
247 slot: &QXYModelMapperPrivate::onModelColumnsRemoved);
248 QObjectPrivate::disconnect(sender: d->m_model,
249 signal: &QAbstractItemModel::modelReset,
250 receiverPrivate: d,
251 slot: &QXYModelMapperPrivate::initializeXYFromModel);
252 QObjectPrivate::disconnect(sender: d->m_model,
253 signal: &QAbstractItemModel::layoutChanged,
254 receiverPrivate: d,
255 slot: &QXYModelMapperPrivate::initializeXYFromModel);
256 QObjectPrivate::disconnect(sender: d->m_model,
257 signal: &QAbstractItemModel::destroyed,
258 receiverPrivate: d,
259 slot: &QXYModelMapperPrivate::handleModelDestroyed);
260 }
261
262 d->m_model = model;
263 d->initializeXYFromModel();
264 // connect signals from the model
265 QObjectPrivate::connect(sender: d->m_model,
266 signal: &QAbstractItemModel::dataChanged,
267 receiverPrivate: d,
268 slot: &QXYModelMapperPrivate::onModelUpdated);
269 QObjectPrivate::connect(sender: d->m_model,
270 signal: &QAbstractItemModel::rowsInserted,
271 receiverPrivate: d,
272 slot: &QXYModelMapperPrivate::onModelRowsAdded);
273 QObjectPrivate::connect(sender: d->m_model,
274 signal: &QAbstractItemModel::rowsRemoved,
275 receiverPrivate: d,
276 slot: &QXYModelMapperPrivate::onModelRowsRemoved);
277 QObjectPrivate::connect(sender: d->m_model,
278 signal: &QAbstractItemModel::columnsInserted,
279 receiverPrivate: d,
280 slot: &QXYModelMapperPrivate::onModelColumnsAdded);
281 QObjectPrivate::connect(sender: d->m_model,
282 signal: &QAbstractItemModel::columnsRemoved,
283 receiverPrivate: d,
284 slot: &QXYModelMapperPrivate::onModelColumnsRemoved);
285
286 QObjectPrivate::connect(sender: d->m_model,
287 signal: &QAbstractItemModel::modelReset,
288 receiverPrivate: d,
289 slot: &QXYModelMapperPrivate::initializeXYFromModel);
290 QObjectPrivate::connect(sender: d->m_model,
291 signal: &QAbstractItemModel::layoutChanged,
292 receiverPrivate: d,
293 slot: &QXYModelMapperPrivate::initializeXYFromModel);
294 QObjectPrivate::connect(sender: d->m_model,
295 signal: &QAbstractItemModel::destroyed,
296 receiverPrivate: d,
297 slot: &QXYModelMapperPrivate::handleModelDestroyed);
298 Q_EMIT modelChanged();
299}
300
301QXYSeries *QXYModelMapper::series() const
302{
303 Q_D(const QXYModelMapper);
304 return d->m_series;
305}
306
307void QXYModelMapper::setSeries(QXYSeries *series)
308{
309 Q_D(QXYModelMapper);
310 if (d->m_series) {
311 QObjectPrivate::disconnect(sender: d->m_series,
312 signal: &QXYSeries::pointAdded,
313 receiverPrivate: d,
314 slot: &QXYModelMapperPrivate::onPointAdded);
315 QObjectPrivate::disconnect(sender: d->m_series,
316 signal: &QXYSeries::pointRemoved,
317 receiverPrivate: d,
318 slot: &QXYModelMapperPrivate::onPointRemoved);
319 QObjectPrivate::disconnect(sender: d->m_series,
320 signal: &QXYSeries::pointReplaced,
321 receiverPrivate: d,
322 slot: &QXYModelMapperPrivate::onPointReplaced);
323 QObjectPrivate::disconnect(sender: d->m_series,
324 signal: &QXYSeries::destroyed,
325 receiverPrivate: d,
326 slot: &QXYModelMapperPrivate::handleSeriesDestroyed);
327 QObjectPrivate::disconnect(sender: d->m_series,
328 signal: &QXYSeries::pointsRemoved,
329 receiverPrivate: d,
330 slot: &QXYModelMapperPrivate::onPointsRemoved);
331 }
332
333 if (series == 0)
334 return;
335
336 d->m_series = series;
337 d->initializeXYFromModel();
338 // connect the signals from the series
339 QObjectPrivate::connect(sender: d->m_series,
340 signal: &QXYSeries::pointAdded,
341 receiverPrivate: d,
342 slot: &QXYModelMapperPrivate::onPointAdded);
343 QObjectPrivate::connect(sender: d->m_series,
344 signal: &QXYSeries::pointRemoved,
345 receiverPrivate: d,
346 slot: &QXYModelMapperPrivate::onPointRemoved);
347 QObjectPrivate::connect(sender: d->m_series,
348 signal: &QXYSeries::pointReplaced,
349 receiverPrivate: d,
350 slot: &QXYModelMapperPrivate::onPointReplaced);
351 QObjectPrivate::connect(sender: d->m_series,
352 signal: &QXYSeries::destroyed,
353 receiverPrivate: d,
354 slot: &QXYModelMapperPrivate::handleSeriesDestroyed);
355 QObjectPrivate::connect(sender: d->m_series,
356 signal: &QXYSeries::pointsRemoved,
357 receiverPrivate: d,
358 slot: &QXYModelMapperPrivate::onPointsRemoved);
359 Q_EMIT seriesChanged();
360}
361
362qsizetype QXYModelMapper::first() const
363{
364 Q_D(const QXYModelMapper);
365 return d->m_first;
366}
367
368void QXYModelMapper::setFirst(qsizetype first)
369{
370 Q_D(QXYModelMapper);
371 d->m_first = qMax(a: first, b: 0);
372 d->initializeXYFromModel();
373 Q_EMIT firstChanged();
374}
375
376qsizetype QXYModelMapper::count() const
377{
378 Q_D(const QXYModelMapper);
379 return d->m_count;
380}
381
382void QXYModelMapper::setCount(qsizetype count)
383{
384 Q_D(QXYModelMapper);
385 d->m_count = qMax(a: count, b: -1);
386 d->initializeXYFromModel();
387 Q_EMIT countChanged();
388}
389
390Qt::Orientation QXYModelMapper::orientation() const
391{
392 Q_D(const QXYModelMapper);
393 return d->m_orientation;
394}
395
396void QXYModelMapper::setOrientation(Qt::Orientation orientation)
397{
398 Q_D(QXYModelMapper);
399 d->m_orientation = orientation;
400 d->initializeXYFromModel();
401 Q_EMIT orientationChanged();
402}
403
404qsizetype QXYModelMapper::xSection() const
405{
406 Q_D(const QXYModelMapper);
407 return d->m_xSection;
408}
409
410void QXYModelMapper::setXSection(qsizetype xSection)
411{
412 Q_D(QXYModelMapper);
413 d->m_xSection = qMax(a: -1, b: xSection);
414 d->initializeXYFromModel();
415 Q_EMIT xSectionChanged();
416}
417
418qsizetype QXYModelMapper::ySection() const
419{
420 Q_D(const QXYModelMapper);
421 return d->m_ySection;
422}
423
424void QXYModelMapper::setYSection(qsizetype ySection)
425{
426 Q_D(QXYModelMapper);
427 d->m_ySection = qMax(a: -1, b: ySection);
428 d->initializeXYFromModel();
429 Q_EMIT ySectionChanged();
430}
431
432///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
433
434QXYModelMapperPrivate::QXYModelMapperPrivate() {}
435
436QXYModelMapperPrivate::~QXYModelMapperPrivate() {}
437
438void QXYModelMapperPrivate::blockModelSignals(bool block)
439{
440 m_modelSignalsBlock = block;
441}
442
443void QXYModelMapperPrivate::blockSeriesSignals(bool block)
444{
445 m_seriesSignalsBlock = block;
446}
447
448QModelIndex QXYModelMapperPrivate::xModelIndex(qsizetype xIndex)
449{
450 if (m_count != -1 && xIndex >= m_count)
451 return QModelIndex(); // invalid
452
453 if (m_orientation == Qt::Vertical)
454 return m_model->index(row: int(xIndex) + m_first, column: m_xSection);
455 else
456 return m_model->index(row: m_xSection, column: int(xIndex) + m_first);
457}
458
459QModelIndex QXYModelMapperPrivate::yModelIndex(qsizetype yIndex)
460{
461 if (m_count != -1 && yIndex >= m_count)
462 return QModelIndex(); // invalid
463
464 if (m_orientation == Qt::Vertical)
465 return m_model->index(row: int(yIndex) + m_first, column: m_ySection);
466 else
467 return m_model->index(row: m_ySection, column: int(yIndex) + m_first);
468}
469
470qreal QXYModelMapperPrivate::valueFromModel(QModelIndex index)
471{
472 QVariant value = m_model->data(index, role: Qt::DisplayRole);
473 switch (value.metaType().id()) {
474 case QMetaType::QDateTime:
475 return value.toDateTime().toMSecsSinceEpoch();
476 case QMetaType::QDate:
477 return value.toDate().startOfDay().toMSecsSinceEpoch();
478 default:
479 return value.toReal();
480 }
481}
482
483void QXYModelMapperPrivate::setValueToModel(QModelIndex index, qreal value)
484{
485 QVariant oldValue = m_model->data(index, role: Qt::DisplayRole);
486 switch (oldValue.metaType().id()) {
487 case QMetaType::QDateTime:
488 m_model->setData(index, value: QDateTime::fromMSecsSinceEpoch(msecs: value));
489 break;
490 case QMetaType::QDate:
491 m_model->setData(index, value: QDateTime::fromMSecsSinceEpoch(msecs: value).date());
492 break;
493 default:
494 m_model->setData(index, value);
495 }
496}
497
498void QXYModelMapperPrivate::onPointAdded(qsizetype pointIndex)
499{
500 if (m_seriesSignalsBlock)
501 return;
502
503 if (m_count != -1)
504 m_count += 1;
505
506 blockModelSignals();
507 if (m_orientation == Qt::Vertical)
508 m_model->insertRows(row: int(pointIndex) + m_first, count: 1);
509 else
510 m_model->insertColumns(column: int(pointIndex) + m_first, count: 1);
511
512 setValueToModel(index: xModelIndex(xIndex: pointIndex), value: m_series->points().at(i: pointIndex).x());
513 setValueToModel(index: yModelIndex(yIndex: pointIndex), value: m_series->points().at(i: pointIndex).y());
514 blockModelSignals(block: false);
515}
516
517void QXYModelMapperPrivate::onPointRemoved(qsizetype pointIndex)
518{
519 if (m_seriesSignalsBlock)
520 return;
521
522 if (m_count != -1)
523 m_count -= 1;
524
525 blockModelSignals();
526 if (m_orientation == Qt::Vertical)
527 m_model->removeRow(arow: int(pointIndex) + m_first);
528 else
529 m_model->removeColumn(acolumn: int(pointIndex) + m_first);
530 blockModelSignals(block: false);
531}
532
533void QXYModelMapperPrivate::onPointsRemoved(qsizetype pointIndex, qsizetype count)
534{
535 if (m_seriesSignalsBlock)
536 return;
537
538 m_count -= count;
539
540 if (m_count < -1)
541 m_count = -1;
542
543 blockModelSignals();
544 if (m_orientation == Qt::Vertical)
545 m_model->removeRows(row: int(pointIndex) + m_first, count: int(count));
546 else
547 m_model->removeColumns(column: int(pointIndex) + m_first, count: int(count));
548 blockModelSignals(block: false);
549}
550
551void QXYModelMapperPrivate::onPointReplaced(qsizetype pointIndex)
552{
553 if (m_seriesSignalsBlock)
554 return;
555
556 blockModelSignals();
557 setValueToModel(index: xModelIndex(xIndex: pointIndex), value: m_series->points().at(i: pointIndex).x());
558 setValueToModel(index: yModelIndex(yIndex: pointIndex), value: m_series->points().at(i: pointIndex).y());
559 blockModelSignals(block: false);
560}
561
562void QXYModelMapperPrivate::handleSeriesDestroyed()
563{
564 m_series = 0;
565}
566
567void QXYModelMapperPrivate::onModelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
568{
569 if (m_model == 0 || m_series == 0)
570 return;
571
572 if (m_modelSignalsBlock)
573 return;
574
575 blockSeriesSignals();
576 QModelIndex index;
577 QPointF newPoint;
578 int indexColumn = 0;
579 int indexRow = 0;
580 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
581 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
582 index = topLeft.sibling(arow: row, acolumn: column);
583 indexColumn = index.column();
584 indexRow = index.row();
585 if (m_orientation == Qt::Vertical
586 && (indexColumn == m_xSection || indexColumn == m_ySection)) {
587 if (indexRow >= m_first && (m_count == -1 || indexRow < m_first + m_count)) {
588 QModelIndex xIndex = xModelIndex(xIndex: indexRow - m_first);
589 QModelIndex yIndex = yModelIndex(yIndex: indexRow - m_first);
590 if (xIndex.isValid() && yIndex.isValid()) {
591 newPoint.setX(valueFromModel(index: xIndex));
592 newPoint.setY(valueFromModel(index: yIndex));
593 m_series->replace(index: indexRow - m_first, newPoint);
594 }
595 }
596 } else if (m_orientation == Qt::Horizontal
597 && (indexRow == m_xSection || indexRow == m_ySection)) {
598 if (indexColumn >= m_first && (m_count == -1 || indexColumn < m_first + m_count)) {
599 QModelIndex xIndex = xModelIndex(xIndex: indexColumn - m_first);
600 QModelIndex yIndex = yModelIndex(yIndex: indexColumn - m_first);
601 if (xIndex.isValid() && yIndex.isValid()) {
602 newPoint.setX(valueFromModel(index: xIndex));
603 newPoint.setY(valueFromModel(index: yIndex));
604 m_series->replace(index: indexColumn - m_first, newPoint);
605 }
606 }
607 }
608 }
609 }
610 blockSeriesSignals(block: false);
611}
612
613void QXYModelMapperPrivate::onModelRowsAdded(QModelIndex parent, qsizetype start, qsizetype end)
614{
615 Q_UNUSED(parent);
616 if (m_modelSignalsBlock)
617 return;
618
619 blockSeriesSignals();
620 if (m_orientation == Qt::Vertical) {
621 insertData(start, end);
622 } else if (start <= m_xSection || start <= m_ySection) {
623 // if the changes affect the map - reinitialize the xy
624 initializeXYFromModel();
625 }
626 blockSeriesSignals(block: false);
627}
628
629void QXYModelMapperPrivate::onModelRowsRemoved(QModelIndex parent, qsizetype start, qsizetype end)
630{
631 Q_UNUSED(parent);
632 if (m_modelSignalsBlock)
633 return;
634
635 blockSeriesSignals();
636 if (m_orientation == Qt::Vertical) {
637 removeData(start, end);
638 } else if (start <= m_xSection || start <= m_ySection) {
639 // if the changes affect the map - reinitialize the xy
640 initializeXYFromModel();
641 }
642 blockSeriesSignals(block: false);
643}
644
645void QXYModelMapperPrivate::onModelColumnsAdded(QModelIndex parent, qsizetype start, qsizetype end)
646{
647 Q_UNUSED(parent);
648 if (m_modelSignalsBlock)
649 return;
650
651 blockSeriesSignals();
652 if (m_orientation == Qt::Horizontal) {
653 insertData(start, end);
654 } else if (start <= m_xSection || start <= m_ySection) {
655 // if the changes affect the map - reinitialize the xy
656 initializeXYFromModel();
657 }
658 blockSeriesSignals(block: false);
659}
660
661void QXYModelMapperPrivate::onModelColumnsRemoved(QModelIndex parent, qsizetype start, qsizetype end)
662{
663 Q_UNUSED(parent);
664 if (m_modelSignalsBlock)
665 return;
666
667 blockSeriesSignals();
668 if (m_orientation == Qt::Horizontal) {
669 removeData(start, end);
670 } else if (start <= m_xSection || start <= m_ySection) {
671 // if the changes affect the map - reinitialize the xy
672 initializeXYFromModel();
673 }
674 blockSeriesSignals(block: false);
675}
676
677void QXYModelMapperPrivate::handleModelDestroyed()
678{
679 m_model = 0;
680}
681
682void QXYModelMapperPrivate::insertData(int start, int end)
683{
684 if (m_model == 0 || m_series == 0)
685 return;
686
687 if (m_count != -1 && start >= m_first + m_count) {
688 return;
689 } else {
690 int addedCount = end - start + 1;
691 if (m_count != -1 && addedCount > m_count)
692 addedCount = m_count;
693 int first = qMax(a: start, b: m_first);
694 int last = qMin(a: first + addedCount - 1,
695 b: m_orientation == Qt::Vertical ? m_model->rowCount() - 1
696 : m_model->columnCount() - 1);
697 for (int i = first; i <= last; i++) {
698 QPointF point;
699 QModelIndex xIndex = xModelIndex(xIndex: i - m_first);
700 QModelIndex yIndex = yModelIndex(yIndex: i - m_first);
701 if (xIndex.isValid() && yIndex.isValid()) {
702 point.setX(valueFromModel(index: xIndex));
703 point.setY(valueFromModel(index: yIndex));
704 m_series->insert(index: i - m_first, point);
705 }
706 }
707
708 // remove excess of points (above m_count)
709 if (m_count != -1 && m_series->points().size() > m_count) {
710 for (qsizetype i = m_series->points().size() - 1; i >= m_count; i--)
711 m_series->remove(point: m_series->points().at(i));
712 }
713 }
714}
715
716void QXYModelMapperPrivate::removeData(int start, int end)
717{
718 if (m_model == 0 || m_series == 0)
719 return;
720
721 int removedCount = end - start + 1;
722 if (m_count != -1 && start >= m_first + m_count) {
723 return;
724 } else {
725 int toRemove = qMin(a: int(m_series->count()),
726 b: removedCount); // first find how many items can actually be removed
727 int first = qMax(a: start, b: m_first); // get the index of the first item that will be removed.
728 int last = qMin(a: first + toRemove - 1,
729 b: int(m_series->count()) + m_first
730 - 1); // get the index of the last item that will be removed.
731 for (int i = last; i >= first; i--)
732 m_series->remove(point: m_series->points().at(i: i - m_first));
733
734 if (m_count != -1) {
735 qsizetype itemsAvailable; // check how many are available to be added
736 if (m_orientation == Qt::Vertical)
737 itemsAvailable = m_model->rowCount() - m_first - m_series->count();
738 else
739 itemsAvailable = m_model->columnCount() - m_first - m_series->count();
740 int toBeAdded = qMin(
741 a: int(itemsAvailable),
742 b: m_count
743 - int(m_series->count())); // add not more items than there is space left to be filled.
744 qsizetype currentSize = m_series->count();
745 if (toBeAdded > 0) {
746 for (qsizetype i = m_series->count(); i < currentSize + toBeAdded; i++) {
747 QPointF point;
748 QModelIndex xIndex = xModelIndex(xIndex: i);
749 QModelIndex yIndex = yModelIndex(yIndex: i);
750 if (xIndex.isValid() && yIndex.isValid()) {
751 point.setX(valueFromModel(index: xIndex));
752 point.setY(valueFromModel(index: yIndex));
753 m_series->insert(index: i, point);
754 }
755 }
756 }
757 }
758 }
759}
760
761void QXYModelMapperPrivate::initializeXYFromModel()
762{
763 if (m_model == 0 || m_series == 0)
764 return;
765
766 blockSeriesSignals();
767 // clear current content
768 m_series->clear();
769
770 // create the initial points set
771 int pointPos = 0;
772 QModelIndex xIndex = xModelIndex(xIndex: pointPos);
773 QModelIndex yIndex = yModelIndex(yIndex: pointPos);
774
775 if (xIndex.isValid() && yIndex.isValid()) {
776 while (xIndex.isValid() && yIndex.isValid()) {
777 QPointF point;
778 point.setX(valueFromModel(index: xIndex));
779 point.setY(valueFromModel(index: yIndex));
780 m_series->append(point);
781 pointPos++;
782 xIndex = xModelIndex(xIndex: pointPos);
783 yIndex = yModelIndex(yIndex: pointPos);
784 // Don't warn about invalid index after the first, those are valid and used to
785 // determine when we should end looping.
786 }
787 } else {
788 // Invalid index right off the bat means series will be left empty, so output a warning,
789 // unless model is also empty
790 int count = m_orientation == Qt::Vertical ? m_model->rowCount() : m_model->columnCount();
791 if (count > 0) {
792 if (!xIndex.isValid()) {
793 qWarning(msg: "%ls Invalid X coordinate index in model mapper.",
794 qUtf16Printable(QString::fromUtf8(__func__)));
795 } else if (!yIndex.isValid()) {
796 qWarning(msg: "%ls Invalid Y coordinate index in model mapper.",
797 qUtf16Printable(QString::fromUtf8(__func__)));
798 }
799 }
800 }
801
802 blockSeriesSignals(block: false);
803}
804QT_END_NAMESPACE
805

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtgraphs/src/graphs2d/xychart/qxymodelmapper.cpp