1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCharts/QCandlestickModelMapper>
5#include <QtCharts/QCandlestickSeries>
6#include <QtCharts/QCandlestickSet>
7#include <QtCore/QAbstractItemModel>
8#include <private/qcandlestickmodelmapper_p.h>
9
10#include <algorithm>
11
12QT_BEGIN_NAMESPACE
13
14/*!
15 \class QCandlestickModelMapper
16 \since 5.8
17 \inmodule QtCharts
18 \brief Abstract model mapper class for candlestick series.
19
20 Model mappers allow the use of a QAbstractItemModel-derived model as a data source for a chart
21 series, creating a connection between a QCandlestickSeries and the model object. A model mapper
22 maintains an equal size across all \l {QCandlestickSet} {QCandlestickSets}.
23
24 \note The model used must support adding and removing rows/columns and modifying the data of the
25 cells.
26*/
27
28/*!
29 \property QCandlestickModelMapper::model
30 \brief Defines the model that is used by the mapper.
31*/
32
33/*!
34 \property QCandlestickModelMapper::series
35 \brief Defines the QCandlestickSeries object that is used by the mapper.
36
37 \note All data in the series is discarded when it is set to the mapper. When a new series is
38 specified, the old series is disconnected (preserving its data).
39*/
40
41/*!
42 \fn Qt::Orientation QCandlestickModelMapper::orientation() const
43 Returns the orientation that is used when QCandlestickModelMapper accesses the model. This
44 determines whether the consecutive values of the set are read from rows (Qt::Horizontal) or from
45 columns (Qt::Vertical).
46*/
47
48/*!
49 \fn void QCandlestickModelMapper::modelReplaced()
50 \brief Emitted when the model, to which the mapper is connected, has changed.
51 \sa model
52*/
53
54/*!
55 \fn void QCandlestickModelMapper::seriesReplaced()
56 \brief Emitted when the series to which mapper is connected to has changed.
57 \sa series
58*/
59
60/*!
61 Constructs a model mapper object as a child of \a parent.
62*/
63QCandlestickModelMapper::QCandlestickModelMapper(QObject *parent)
64 : QObject(parent),
65 d_ptr(new QCandlestickModelMapperPrivate(this))
66{
67}
68
69void QCandlestickModelMapper::setModel(QAbstractItemModel *model)
70{
71 Q_D(QCandlestickModelMapper);
72
73 if (d->m_model == model)
74 return;
75
76 if (d->m_model)
77 disconnect(sender: d->m_model, signal: 0, receiver: d, member: 0);
78
79 d->m_model = model;
80 emit modelReplaced();
81
82 if (!d->m_model)
83 return;
84
85 d->initializeCandlestickFromModel();
86 // connect signals from the model
87 connect(sender: d->m_model, SIGNAL(modelReset()), receiver: d, SLOT(initializeCandlestickFromModel()));
88 connect(sender: d->m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
89 receiver: d, SLOT(modelDataUpdated(QModelIndex, QModelIndex)));
90 connect(sender: d->m_model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
91 receiver: d, SLOT(modelHeaderDataUpdated(Qt::Orientation, int, int)));
92 connect(sender: d->m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
93 receiver: d, SLOT(modelRowsInserted(QModelIndex, int, int)));
94 connect(sender: d->m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
95 receiver: d, SLOT(modelRowsRemoved(QModelIndex, int, int)));
96 connect(sender: d->m_model, SIGNAL(columnsInserted(QModelIndex, int, int)),
97 receiver: d, SLOT(modelColumnsInserted(QModelIndex, int, int)));
98 connect(sender: d->m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)),
99 receiver: d, SLOT(modelColumnsRemoved(QModelIndex, int, int)));
100 connect(sender: d->m_model, SIGNAL(destroyed()), receiver: d, SLOT(modelDestroyed()));
101}
102
103QAbstractItemModel *QCandlestickModelMapper::model() const
104{
105 Q_D(const QCandlestickModelMapper);
106
107 return d->m_model;
108}
109
110void QCandlestickModelMapper::setSeries(QCandlestickSeries *series)
111{
112 Q_D(QCandlestickModelMapper);
113
114 if (d->m_series == series)
115 return;
116
117 if (d->m_series)
118 disconnect(sender: d->m_series, signal: 0, receiver: d, member: 0);
119
120 d->m_series = series;
121 emit seriesReplaced();
122
123 if (!d->m_series)
124 return;
125
126 d->initializeCandlestickFromModel();
127 // connect the signals from the series
128 connect(sender: d->m_series, SIGNAL(candlestickSetsAdded(QList<QCandlestickSet *>)),
129 receiver: d, SLOT(candlestickSetsAdded(QList<QCandlestickSet *>)));
130 connect(sender: d->m_series, SIGNAL(candlestickSetsRemoved(QList<QCandlestickSet*>)),
131 receiver: d, SLOT(candlestickSetsRemoved(QList<QCandlestickSet *>)));
132 connect(sender: d->m_series, SIGNAL(destroyed()), receiver: d, SLOT(seriesDestroyed()));
133}
134
135QCandlestickSeries *QCandlestickModelMapper::series() const
136{
137 Q_D(const QCandlestickModelMapper);
138
139 return d->m_series;
140}
141
142/*!
143 Sets the row/column of the model that contains the \a timestamp values of the sets in the
144 series. Default value is -1 (invalid mapping).
145*/
146void QCandlestickModelMapper::setTimestamp(int timestamp)
147{
148 Q_D(QCandlestickModelMapper);
149
150 timestamp = qMax(a: timestamp, b: -1);
151
152 if (d->m_timestamp == timestamp)
153 return;
154
155 d->m_timestamp = timestamp;
156 emit d->timestampChanged();
157 d->initializeCandlestickFromModel();
158}
159
160/*!
161 Returns the row/column of the model that contains the timestamp values of the sets in the
162 series. Default value is -1 (invalid mapping).
163*/
164int QCandlestickModelMapper::timestamp() const
165{
166 Q_D(const QCandlestickModelMapper);
167
168 return d->m_timestamp;
169}
170
171/*!
172 Sets the row/column of the model that contains the \a open values of the sets in the series.
173 Default value is -1 (invalid mapping).
174*/
175void QCandlestickModelMapper::setOpen(int open)
176{
177 Q_D(QCandlestickModelMapper);
178
179 open = qMax(a: open, b: -1);
180
181 if (d->m_open == open)
182 return;
183
184 d->m_open = open;
185 emit d->openChanged();
186 d->initializeCandlestickFromModel();
187}
188
189/*!
190 Returns the row/column of the model that contains the open values of the sets in the series.
191 Default value is -1 (invalid mapping).
192*/
193int QCandlestickModelMapper::open() const
194{
195 Q_D(const QCandlestickModelMapper);
196
197 return d->m_open;
198}
199
200/*!
201 Sets the row/column of the model that contains the \a high values of the sets in the series.
202 Default value is -1 (invalid mapping).
203*/
204void QCandlestickModelMapper::setHigh(int high)
205{
206 Q_D(QCandlestickModelMapper);
207
208 high = qMax(a: high, b: -1);
209
210 if (d->m_high == high)
211 return;
212
213 d->m_high = high;
214 emit d->highChanged();
215 d->initializeCandlestickFromModel();
216}
217
218/*!
219 Returns the row/column of the model that contains the high values of the sets in the series.
220 Default value is -1 (invalid mapping).
221*/
222int QCandlestickModelMapper::high() const
223{
224 Q_D(const QCandlestickModelMapper);
225
226 return d->m_high;
227}
228
229/*!
230 Sets the row/column of the model that contains the \a low values of the sets in the series.
231 Default value is -1 (invalid mapping).
232*/
233void QCandlestickModelMapper::setLow(int low)
234{
235 Q_D(QCandlestickModelMapper);
236
237 low = qMax(a: low, b: -1);
238
239 if (d->m_low == low)
240 return;
241
242 d->m_low = low;
243 emit d->lowChanged();
244 d->initializeCandlestickFromModel();
245}
246
247/*!
248 Returns the row/column of the model that contains the low values of the sets in the series.
249 Default value is -1 (invalid mapping).
250*/
251int QCandlestickModelMapper::low() const
252{
253 Q_D(const QCandlestickModelMapper);
254
255 return d->m_low;
256}
257
258/*!
259 Sets the row/column of the model that contains the \a close values of the sets in the series.
260 Default value is -1 (invalid mapping).
261*/
262void QCandlestickModelMapper::setClose(int close)
263{
264 Q_D(QCandlestickModelMapper);
265
266 close = qMax(a: close, b: -1);
267
268 if (d->m_close == close)
269 return;
270
271 d->m_close = close;
272 emit d->closeChanged();
273 d->initializeCandlestickFromModel();
274}
275
276/*!
277 Returns the row/column of the model that contains the close values of the sets in the series.
278 Default value is -1 (invalid mapping).
279*/
280int QCandlestickModelMapper::close() const
281{
282 Q_D(const QCandlestickModelMapper);
283
284 return d->m_close;
285}
286
287/*!
288 Sets the section of the model that is used as the data source for the first candlestick set.
289 Parameter \a firstSetSection specifies the section of the model. Default value is -1.
290*/
291void QCandlestickModelMapper::setFirstSetSection(int firstSetSection)
292{
293 Q_D(QCandlestickModelMapper);
294
295 firstSetSection = qMax(a: firstSetSection, b: -1);
296
297 if (d->m_firstSetSection == firstSetSection)
298 return;
299
300 d->m_firstSetSection = firstSetSection;
301 emit d->firstSetSectionChanged();
302 d->initializeCandlestickFromModel();
303}
304
305/*!
306 Returns the section of the model that is used as the data source for the first candlestick set.
307 Default value is -1 (invalid mapping).
308*/
309int QCandlestickModelMapper::firstSetSection() const
310{
311 Q_D(const QCandlestickModelMapper);
312
313 return d->m_firstSetSection;
314}
315
316/*!
317 Sets the section of the model that is used as the data source for the last candlestick set.
318 Parameter \a lastSetSection specifies the section of the model. Default value is -1.
319*/
320void QCandlestickModelMapper::setLastSetSection(int lastSetSection)
321{
322 Q_D(QCandlestickModelMapper);
323
324 lastSetSection = qMax(a: lastSetSection, b: -1);
325
326 if (d->m_lastSetSection == lastSetSection)
327 return;
328
329 d->m_lastSetSection = lastSetSection;
330 emit d->lastSetSectionChanged();
331 d->initializeCandlestickFromModel();
332}
333
334/*!
335 Returns the section of the model that is used as the data source for the last candlestick set.
336 Default value is -1 (invalid mapping).
337*/
338int QCandlestickModelMapper::lastSetSection() const
339{
340 Q_D(const QCandlestickModelMapper);
341
342 return d->m_lastSetSection;
343}
344
345////////////////////////////////////////////////////////////////////////////////////////////////////
346
347QCandlestickModelMapperPrivate::QCandlestickModelMapperPrivate(QCandlestickModelMapper *q)
348 : QObject(q),
349 m_model(nullptr),
350 m_series(nullptr),
351 m_timestamp(-1),
352 m_open(-1),
353 m_high(-1),
354 m_low(-1),
355 m_close(-1),
356 m_firstSetSection(-1),
357 m_lastSetSection(-1),
358 m_modelSignalsBlock(false),
359 m_seriesSignalsBlock(false),
360 q_ptr(q)
361{
362}
363
364void QCandlestickModelMapperPrivate::initializeCandlestickFromModel()
365{
366 if (!m_model || !m_series)
367 return;
368
369 blockSeriesSignals();
370 // clear current content
371 m_series->clear();
372 m_sets.clear();
373
374 // create the initial candlestick sets
375 QList<QCandlestickSet *> sets;
376 for (int i = m_firstSetSection; i <= m_lastSetSection; ++i) {
377 QModelIndex timestampIndex = candlestickModelIndex(section: i, pos: m_timestamp);
378 QModelIndex openIndex = candlestickModelIndex(section: i, pos: m_open);
379 QModelIndex highIndex = candlestickModelIndex(section: i, pos: m_high);
380 QModelIndex lowIndex = candlestickModelIndex(section: i, pos: m_low);
381 QModelIndex closeIndex = candlestickModelIndex(section: i, pos: m_close);
382 if (timestampIndex.isValid()
383 && openIndex.isValid()
384 && highIndex.isValid()
385 && lowIndex.isValid()
386 && closeIndex.isValid()) {
387 QCandlestickSet *set = new QCandlestickSet();
388 set->setTimestamp(m_model->data(index: timestampIndex, role: Qt::DisplayRole).toReal());
389 set->setOpen(m_model->data(index: openIndex, role: Qt::DisplayRole).toReal());
390 set->setHigh(m_model->data(index: highIndex, role: Qt::DisplayRole).toReal());
391 set->setLow(m_model->data(index: lowIndex, role: Qt::DisplayRole).toReal());
392 set->setClose(m_model->data(index: closeIndex, role: Qt::DisplayRole).toReal());
393
394 connect(sender: set, SIGNAL(timestampChanged()), receiver: this, SLOT(candlestickSetChanged()));
395 connect(sender: set, SIGNAL(openChanged()), receiver: this, SLOT(candlestickSetChanged()));
396 connect(sender: set, SIGNAL(highChanged()), receiver: this, SLOT(candlestickSetChanged()));
397 connect(sender: set, SIGNAL(lowChanged()), receiver: this, SLOT(candlestickSetChanged()));
398 connect(sender: set, SIGNAL(closeChanged()), receiver: this, SLOT(candlestickSetChanged()));
399
400 sets.append(t: set);
401 } else {
402 break;
403 }
404 }
405 m_series->append(sets);
406 m_sets.append(l: sets);
407 blockSeriesSignals(block: false);
408}
409
410void QCandlestickModelMapperPrivate::modelDataUpdated(QModelIndex topLeft, QModelIndex bottomRight)
411{
412 Q_Q(QCandlestickModelMapper);
413
414 if (!m_model || !m_series)
415 return;
416
417 if (m_modelSignalsBlock)
418 return;
419
420 blockSeriesSignals();
421 QModelIndex index;
422 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
423 for (int column = topLeft.column(); column <= bottomRight.column(); ++column) {
424 index = topLeft.sibling(arow: row, acolumn: column);
425 QCandlestickSet *set = candlestickSet(index);
426 if (set) {
427 int pos = (q->orientation() == Qt::Vertical) ? row : column;
428 if (pos == m_timestamp)
429 set->setTimestamp(m_model->data(index).toReal());
430 else if (pos == m_open)
431 set->setOpen(m_model->data(index).toReal());
432 else if (pos == m_high)
433 set->setHigh(m_model->data(index).toReal());
434 else if (pos == m_low)
435 set->setLow(m_model->data(index).toReal());
436 else if (pos == m_close)
437 set->setClose(m_model->data(index).toReal());
438 }
439 }
440 }
441 blockSeriesSignals(block: false);
442}
443
444void QCandlestickModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first,
445 int last)
446{
447 Q_UNUSED(orientation);
448 Q_UNUSED(first);
449 Q_UNUSED(last);
450}
451
452void QCandlestickModelMapperPrivate::modelRowsInserted(QModelIndex parent, int start, int end)
453{
454 Q_UNUSED(parent);
455
456 Q_Q(QCandlestickModelMapper);
457
458 if (m_modelSignalsBlock)
459 return;
460
461 blockSeriesSignals();
462 if (q->orientation() == Qt::Vertical)
463 insertData(start, end);
464 else if (start <= m_firstSetSection || start <= m_lastSetSection)
465 initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
466 blockSeriesSignals(block: false);
467}
468
469void QCandlestickModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end)
470{
471 Q_UNUSED(parent);
472
473 Q_Q(QCandlestickModelMapper);
474
475 if (m_modelSignalsBlock)
476 return;
477
478 blockSeriesSignals();
479 if (q->orientation() == Qt::Vertical)
480 removeData(start, end);
481 else if (start <= m_firstSetSection || start <= m_lastSetSection)
482 initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
483 blockSeriesSignals(block: false);
484}
485
486void QCandlestickModelMapperPrivate::modelColumnsInserted(QModelIndex parent, int start, int end)
487{
488 Q_UNUSED(parent);
489
490 Q_Q(QCandlestickModelMapper);
491
492 if (m_modelSignalsBlock)
493 return;
494
495 blockSeriesSignals();
496 if (q->orientation() == Qt::Horizontal)
497 insertData(start, end);
498 else if (start <= m_firstSetSection || start <= m_lastSetSection)
499 initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
500 blockSeriesSignals(block: false);
501}
502
503void QCandlestickModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end)
504{
505 Q_UNUSED(parent);
506
507 Q_Q(QCandlestickModelMapper);
508
509 if (m_modelSignalsBlock)
510 return;
511
512 blockSeriesSignals();
513 if (q->orientation() == Qt::Horizontal)
514 removeData(start, end);
515 else if (start <= m_firstSetSection || start <= m_lastSetSection)
516 initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
517 blockSeriesSignals(block: false);
518}
519
520void QCandlestickModelMapperPrivate::modelDestroyed()
521{
522 m_model = 0;
523}
524
525void QCandlestickModelMapperPrivate::candlestickSetsAdded(const QList<QCandlestickSet *> &sets)
526{
527 Q_Q(QCandlestickModelMapper);
528
529 if (m_seriesSignalsBlock)
530 return;
531
532 if (sets.isEmpty())
533 return;
534
535 int firstIndex = m_series->sets().indexOf(t: sets.at(i: 0));
536 if (firstIndex == -1)
537 return;
538
539 m_lastSetSection += sets.size();
540
541 blockModelSignals();
542 if (q->orientation() == Qt::Vertical)
543 m_model->insertColumns(column: firstIndex + m_firstSetSection, count: sets.size());
544 else
545 m_model->insertRows(row: firstIndex + m_firstSetSection, count: sets.size());
546
547 for (int i = 0; i < sets.size(); ++i) {
548 int section = i + firstIndex + m_firstSetSection;
549 m_model->setData(index: candlestickModelIndex(section, pos: m_timestamp), value: sets.at(i)->timestamp());
550 m_model->setData(index: candlestickModelIndex(section, pos: m_open), value: sets.at(i)->open());
551 m_model->setData(index: candlestickModelIndex(section, pos: m_high), value: sets.at(i)->high());
552 m_model->setData(index: candlestickModelIndex(section, pos: m_low), value: sets.at(i)->low());
553 m_model->setData(index: candlestickModelIndex(section, pos: m_close), value: sets.at(i)->close());
554 }
555 blockModelSignals(block: false);
556 initializeCandlestickFromModel();
557}
558
559void QCandlestickModelMapperPrivate::candlestickSetsRemoved(const QList<QCandlestickSet *> &sets)
560{
561 Q_Q(QCandlestickModelMapper);
562
563 if (m_seriesSignalsBlock)
564 return;
565
566 if (sets.isEmpty())
567 return;
568
569 QList<int> removedIndices;
570 for (auto &set : sets) {
571 int index = m_sets.indexOf(t: set);
572 if (index != -1)
573 removedIndices << index;
574 }
575
576 if (removedIndices.isEmpty())
577 return;
578
579 std::sort(first: removedIndices.begin(), last: removedIndices.end());
580
581 for (int i = removedIndices.size() - 1; i >= 0; --i) {
582 m_sets.removeAt(i: removedIndices[i]);
583 --m_lastSetSection;
584 }
585
586 blockModelSignals();
587
588 // There is no guarantee removed sets are continuous, so remove them one by one
589 for (int i = removedIndices.size() - 1; i >= 0; --i) {
590 if (q->orientation() == Qt::Vertical)
591 m_model->removeColumns(column: removedIndices[i] + m_firstSetSection, count: 1);
592 else
593 m_model->removeRows(row: removedIndices[i] + m_firstSetSection, count: 1);
594 }
595
596 blockModelSignals(block: false);
597 initializeCandlestickFromModel();
598}
599
600void QCandlestickModelMapperPrivate::candlestickSetChanged()
601{
602 if (m_seriesSignalsBlock)
603 return;
604
605 QCandlestickSet *set = qobject_cast<QCandlestickSet *>(object: QObject::sender());
606 if (!set)
607 return;
608
609 int section = m_series->sets().indexOf(t: set);
610 if (section < 0)
611 return;
612
613 section += m_firstSetSection;
614
615 blockModelSignals();
616 m_model->setData(index: candlestickModelIndex(section, pos: m_timestamp), value: set->timestamp());
617 m_model->setData(index: candlestickModelIndex(section, pos: m_open), value: set->open());
618 m_model->setData(index: candlestickModelIndex(section, pos: m_high), value: set->high());
619 m_model->setData(index: candlestickModelIndex(section, pos: m_low), value: set->low());
620 m_model->setData(index: candlestickModelIndex(section, pos: m_close), value: set->close());
621 blockModelSignals(block: false);
622}
623
624void QCandlestickModelMapperPrivate::seriesDestroyed()
625{
626 m_series = 0;
627}
628
629QCandlestickSet *QCandlestickModelMapperPrivate::candlestickSet(QModelIndex index)
630{
631 Q_Q(QCandlestickModelMapper);
632
633 if (!index.isValid())
634 return 0;
635
636 int section = (q->orientation() == Qt::Vertical) ? index.column() : index.row();
637 int pos = (q->orientation() == Qt::Vertical) ? index.row() : index.column();
638
639 if (section < m_firstSetSection || section > m_lastSetSection)
640 return 0; // This part of model has not been mapped to any candlestick set.
641
642 if (pos != m_timestamp && pos != m_open && pos != m_high && pos != m_low && pos != m_close)
643 return 0; // This part of model has not been mapped to any candlestick set.
644
645 return m_series->sets().at(i: section - m_firstSetSection);
646}
647
648QModelIndex QCandlestickModelMapperPrivate::candlestickModelIndex(int section, int pos)
649{
650 Q_Q(QCandlestickModelMapper);
651
652 if (section < m_firstSetSection || section > m_lastSetSection)
653 return QModelIndex(); // invalid
654
655 if (pos != m_timestamp && pos != m_open && pos != m_high && pos != m_low && pos != m_close)
656 return QModelIndex(); // invalid
657
658 if (q->orientation() == Qt::Vertical)
659 return m_model->index(row: pos, column: section);
660 else
661 return m_model->index(row: section, column: pos);
662}
663
664void QCandlestickModelMapperPrivate::insertData(int start, int end)
665{
666 Q_UNUSED(start);
667 Q_UNUSED(end);
668
669 // Currently candlestickchart needs to be fully recalculated when change is made.
670 initializeCandlestickFromModel();
671}
672
673void QCandlestickModelMapperPrivate::removeData(int start, int end)
674{
675 Q_UNUSED(start);
676 Q_UNUSED(end);
677
678 // Currently candlestickchart needs to be fully recalculated when change is made.
679 initializeCandlestickFromModel();
680}
681
682void QCandlestickModelMapperPrivate::blockModelSignals(bool block)
683{
684 m_modelSignalsBlock = block;
685}
686
687void QCandlestickModelMapperPrivate::blockSeriesSignals(bool block)
688{
689 m_seriesSignalsBlock = block;
690}
691
692QT_END_NAMESPACE
693
694#include "moc_qcandlestickmodelmapper.cpp"
695#include "moc_qcandlestickmodelmapper_p.cpp"
696

source code of qtcharts/src/charts/candlestickchart/qcandlestickmodelmapper.cpp