From 24716061dcee3820587a533b9c19c18d70815cbe 2013-04-19 15:14:32 From: Mika Salmela Date: 2013-04-19 15:14:32 Subject: [PATCH] BoxPlot model mapper changes Change-Id: I11998e4b0564d4f818e937f48e7ba8231b63af70 Reviewed-by: Mika Salmela --- diff --git a/src/boxplotchart/boxplotchart.pri b/src/boxplotchart/boxplotchart.pri index c0b2327..db6e034 100644 --- a/src/boxplotchart/boxplotchart.pri +++ b/src/boxplotchart/boxplotchart.pri @@ -5,15 +5,21 @@ SOURCES += \ $$PWD/boxplotchartitem.cpp \ $$PWD/qboxplotseries.cpp \ $$PWD/boxwhiskers.cpp \ - $$PWD/qboxset.cpp + $$PWD/qboxset.cpp \ + $$PWD/qboxplotmodelmapper.cpp \ + $$PWD/qvboxplotmodelmapper.cpp PRIVATE_HEADERS += \ $$PWD/boxplotchartitem_p.h \ $$PWD/qboxplotseries_p.h \ $$PWD/boxwhiskers_p.h \ $$PWD/boxwhiskersdata_p.h \ - $$PWD/qboxset_p.h + $$PWD/qboxset_p.h \ + $$PWD/qboxplotmodelmapper_p.h PUBLIC_HEADERS += \ $$PWD/qboxplotseries.h \ - $$PWD/qboxset.h + $$PWD/qboxset.h \ + $$PWD/qboxplotmodelmapper.h \ + $$PWD/qvboxplotmodelmapper.h + diff --git a/src/boxplotchart/qboxplotmodelmapper.cpp b/src/boxplotchart/qboxplotmodelmapper.cpp new file mode 100644 index 0000000..7dc8d03 --- /dev/null +++ b/src/boxplotchart/qboxplotmodelmapper.cpp @@ -0,0 +1,538 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qboxplotmodelmapper.h" +#include "qboxplotmodelmapper_p.h" +#include "qboxplotseries.h" +#include "qboxset.h" +#include "qchart.h" +#include + +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +QBoxPlotModelMapper::QBoxPlotModelMapper(QObject *parent) : + QObject(parent), + d_ptr(new QBoxPlotModelMapperPrivate(this)) +{ +} + +QAbstractItemModel *QBoxPlotModelMapper::model() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_model; +} + +void QBoxPlotModelMapper::setModel(QAbstractItemModel *model) +{ + if (model == 0) + return; + + Q_D(QBoxPlotModelMapper); + if (d->m_model) + disconnect(d->m_model, 0, d, 0); + + d->m_model = model; + d->initializeBoxFromModel(); + // connect signals from the model + connect(d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), d, SLOT(modelUpdated(QModelIndex,QModelIndex))); + connect(d->m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), d, SLOT(modelHeaderDataUpdated(Qt::Orientation,int,int))); + connect(d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), d, SLOT(modelRowsAdded(QModelIndex,int,int))); + connect(d->m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), d, SLOT(modelRowsRemoved(QModelIndex,int,int))); + connect(d->m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), d, SLOT(modelColumnsAdded(QModelIndex,int,int))); + connect(d->m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), d, SLOT(modelColumnsRemoved(QModelIndex,int,int))); + connect(d->m_model, SIGNAL(destroyed()), d, SLOT(handleModelDestroyed())); +} + +QBoxPlotSeries *QBoxPlotModelMapper::series() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_series; +} + +void QBoxPlotModelMapper::setSeries(QBoxPlotSeries *series) +{ + Q_D(QBoxPlotModelMapper); + if (d->m_series) + disconnect(d->m_series, 0, d, 0); + + if (series == 0) + return; + + d->m_series = series; + d->initializeBoxFromModel(); + // connect the signals from the series + connect(d->m_series, SIGNAL(boxsetsAdded(QList)), d, SLOT(boxSetsAdded(QList))); + connect(d->m_series, SIGNAL(boxsetsRemoved(QList)), d, SLOT(boxSetsRemoved(QList))); + connect(d->m_series, SIGNAL(destroyed()), d, SLOT(handleSeriesDestroyed())); +} + +/*! + Returns which row/column of the model contains the first values of the QBoxSets in the series. + The default value is 0. +*/ +int QBoxPlotModelMapper::first() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_first; +} + +/*! + Sets which row of the model contains the \a first values of the QBoxSets in the series. + The default value is 0. +*/ +void QBoxPlotModelMapper::setFirst(int first) +{ + Q_D(QBoxPlotModelMapper); + d->m_first = qMax(first, 0); + d->initializeBoxFromModel(); +} + +/*! + Returns the number of rows/columns of the model that are mapped as the data for QBoxPlotSeries + Minimal and default value is: -1 (count limited by the number of rows/columns in the model) +*/ +int QBoxPlotModelMapper::count() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_count; +} + +/*! + Sets the \a count of rows/columns of the model that are mapped as the data for QBoxPlotSeries + Minimal and default value is: -1 (count limited by the number of rows/columns in the model) +*/ +void QBoxPlotModelMapper::setCount(int count) +{ + Q_D(QBoxPlotModelMapper); + d->m_count = qMax(count, -1); + d->initializeBoxFromModel(); +} + +/*! + Returns the orientation that is used when QBoxPlotModelMapper accesses the model. + This mean whether the consecutive values of the bar set are read from row (Qt::Horizontal) + or from columns (Qt::Vertical) +*/ +Qt::Orientation QBoxPlotModelMapper::orientation() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_orientation; +} + +/*! + Returns the \a orientation that is used when QBoxPlotModelMapper accesses the model. + This mean whether the consecutive values of the pie are read from row (Qt::Horizontal) + or from columns (Qt::Vertical) +*/ +void QBoxPlotModelMapper::setOrientation(Qt::Orientation orientation) +{ + Q_D(QBoxPlotModelMapper); + d->m_orientation = orientation; + d->initializeBoxFromModel(); +} + +/*! + Returns which section of the model is used as the data source for the first box set +*/ +int QBoxPlotModelMapper::firstBoxSetSection() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_firstBoxSetSection; +} + +/*! + Sets the model section that is used as the data source for the first box set + Parameter \a firstBoxSetSection specifies the section of the model. +*/ +void QBoxPlotModelMapper::setFirstBoxSetSection(int firstBoxSetSection) +{ + Q_D(QBoxPlotModelMapper); + d->m_firstBoxSetSection = qMax(-1, firstBoxSetSection); + d->initializeBoxFromModel(); +} + +/*! + Returns which section of the model is used as the data source for the last box set +*/ +int QBoxPlotModelMapper::lastBoxSetSection() const +{ + Q_D(const QBoxPlotModelMapper); + return d->m_lastBoxSetSection; +} + +/*! + Sets the model section that is used as the data source for the last box set + Parameter \a lastBoxSetSection specifies the section of the model. +*/ +void QBoxPlotModelMapper::setLastBoxSetSection(int lastBoxSetSection) +{ + Q_D(QBoxPlotModelMapper); + d->m_lastBoxSetSection = qMax(-1, lastBoxSetSection); + d->initializeBoxFromModel(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +QBoxPlotModelMapperPrivate::QBoxPlotModelMapperPrivate(QBoxPlotModelMapper *q) : + QObject(q), + m_series(0), + m_model(0), + m_first(0), + m_count(-1), + m_orientation(Qt::Vertical), + m_firstBoxSetSection(-1), + m_lastBoxSetSection(-1), + m_seriesSignalsBlock(false), + m_modelSignalsBlock(false), + q_ptr(q) +{ +} + +void QBoxPlotModelMapperPrivate::blockModelSignals(bool block) +{ + m_modelSignalsBlock = block; +} + +void QBoxPlotModelMapperPrivate::blockSeriesSignals(bool block) +{ + m_seriesSignalsBlock = block; +} + +QBoxSet *QBoxPlotModelMapperPrivate::boxSet(QModelIndex index) +{ + if (!index.isValid()) + return 0; + + if (m_orientation == Qt::Vertical && index.column() >= m_firstBoxSetSection && index.column() <= m_lastBoxSetSection) { + if (index.row() >= m_first && (m_count == - 1 || index.row() < m_first + m_count)) { + return m_series->boxSets().at(index.column() - m_firstBoxSetSection); + } + } else if (m_orientation == Qt::Horizontal && index.row() >= m_firstBoxSetSection && index.row() <= m_lastBoxSetSection) { + if (index.column() >= m_first && (m_count == - 1 || index.column() < m_first + m_count)) + return m_series->boxSets().at(index.row() - m_firstBoxSetSection); + } + return 0; // This part of model has not been mapped to any slice +} + +QModelIndex QBoxPlotModelMapperPrivate::boxModelIndex(int boxSection, int posInBar) +{ + if (m_count != -1 && posInBar >= m_count) + return QModelIndex(); // invalid + + if (boxSection < m_firstBoxSetSection || boxSection > m_lastBoxSetSection) + return QModelIndex(); // invalid + + if (m_orientation == Qt::Vertical) + return m_model->index(posInBar + m_first, boxSection); + else + return m_model->index(boxSection, posInBar + m_first); +} + +void QBoxPlotModelMapperPrivate::handleSeriesDestroyed() +{ + m_series = 0; +} + +void QBoxPlotModelMapperPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight) +{ + Q_UNUSED(topLeft) + Q_UNUSED(bottomRight) + + if (m_model == 0 || m_series == 0) + return; + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + QModelIndex index; + for (int row = topLeft.row(); row <= bottomRight.row(); row++) { + for (int column = topLeft.column(); column <= bottomRight.column(); column++) { + index = topLeft.sibling(row, column); + QBoxSet *box = boxSet(index); + if (box) { + if (m_orientation == Qt::Vertical) + box->replace(row - m_first, m_model->data(index).toReal()); + else + box->replace(column - m_first, m_model->data(index).toReal()); + } + } + } + blockSeriesSignals(false); +} + +void QBoxPlotModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first, int last) +{ + Q_UNUSED(orientation); + Q_UNUSED(first); + Q_UNUSED(last); + + qDebug() << "ALERT: QBoxPlotModelMapperPrivate::modelHeaderDataUpdated implement"; +} + +void QBoxPlotModelMapperPrivate::modelRowsAdded(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (m_orientation == Qt::Vertical) + insertData(start, end); + else if (start <= m_firstBoxSetSection || start <= m_lastBoxSetSection) // if the changes affect the map - reinitialize + initializeBoxFromModel(); + blockSeriesSignals(false); +} + +void QBoxPlotModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (m_orientation == Qt::Vertical) + removeData(start, end); + else if (start <= m_firstBoxSetSection || start <= m_lastBoxSetSection) // if the changes affect the map - reinitialize + initializeBoxFromModel(); + blockSeriesSignals(false); +} + +void QBoxPlotModelMapperPrivate::modelColumnsAdded(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (m_orientation == Qt::Horizontal) + insertData(start, end); + else if (start <= m_firstBoxSetSection || start <= m_lastBoxSetSection) // if the changes affect the map - reinitialize + initializeBoxFromModel(); + blockSeriesSignals(false); +} + +void QBoxPlotModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (m_orientation == Qt::Horizontal) + removeData(start, end); + else if (start <= m_firstBoxSetSection || start <= m_lastBoxSetSection) // if the changes affect the map - reinitialize + initializeBoxFromModel(); + blockSeriesSignals(false); +} + +void QBoxPlotModelMapperPrivate::handleModelDestroyed() +{ + m_model = 0; +} + +void QBoxPlotModelMapperPrivate::insertData(int start, int end) +{ + Q_UNUSED(end) + Q_UNUSED(start) + Q_UNUSED(end) + // Currently barchart needs to be fully recalculated when change is made. + // Re-initialize + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::removeData(int start, int end) +{ + Q_UNUSED(end) + Q_UNUSED(start) + Q_UNUSED(end) + // Currently barchart needs to be fully recalculated when change is made. + // Re-initialize + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::boxSetsAdded(QList sets) +{ + if (m_seriesSignalsBlock) + return; + + if (sets.count() == 0) + return; + + int firstIndex = m_series->boxSets().indexOf(sets.at(0)); + if (firstIndex == -1) + return; + + int maxCount = 0; + for (int i = 0; i < sets.count(); i++) { + if (sets.at(i)->count() > m_count) + maxCount = sets.at(i)->count(); + } + + if (m_count != -1 && m_count < maxCount) + m_count = maxCount; + + m_lastBoxSetSection += sets.count(); + + blockModelSignals(); + int modelCapacity = m_orientation == Qt::Vertical ? m_model->rowCount() - m_first : m_model->columnCount() - m_first; + if (maxCount > modelCapacity) { + if (m_orientation == Qt::Vertical) + m_model->insertRows(m_model->rowCount(), maxCount - modelCapacity); + else + m_model->insertColumns(m_model->columnCount(), maxCount - modelCapacity); + } + + if (m_orientation == Qt::Vertical) + m_model->insertColumns(firstIndex + m_firstBoxSetSection, sets.count()); + else + m_model->insertRows(firstIndex + m_firstBoxSetSection, sets.count()); + + + for (int i = firstIndex + m_firstBoxSetSection; i < firstIndex + m_firstBoxSetSection + sets.count(); i++) { + //m_model->setHeaderData(i, m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, sets.at(i - firstIndex - m_firstBoxSetSection)->label()); + for (int j = 0; j < sets.at(i - firstIndex - m_firstBoxSetSection)->count(); j++) + m_model->setData(boxModelIndex(i, j), sets.at(i - firstIndex - m_firstBoxSetSection)->at(j)); + } + blockModelSignals(false); + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::boxSetsRemoved(QList sets) +{ + if (m_seriesSignalsBlock) + return; + + if (sets.count() == 0) + return; + + int firstIndex = m_boxSets.indexOf(sets.at(0)); + if (firstIndex == -1) + return; + + m_lastBoxSetSection -= sets.count(); + + for (int i = firstIndex + sets.count() - 1; i >= firstIndex; i--) + m_boxSets.removeAt(i); + + blockModelSignals(); + if (m_orientation == Qt::Vertical) + m_model->removeColumns(firstIndex + m_firstBoxSetSection, sets.count()); + else + m_model->removeRows(firstIndex + m_firstBoxSetSection, sets.count()); + blockModelSignals(false); + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::valuesAdded(int index, int count) +{ + if (m_seriesSignalsBlock) + return; + + if (m_count != -1) + m_count += count; + + int boxSetIndex = m_boxSets.indexOf(qobject_cast(QObject::sender())); + + blockModelSignals(); + if (m_orientation == Qt::Vertical) + m_model->insertRows(index + m_first, count); + else + m_model->insertColumns(index + m_first, count); + + for (int j = index; j < index + count; j++) + m_model->setData(boxModelIndex(boxSetIndex + m_firstBoxSetSection, j), m_boxSets.at(boxSetIndex)->at(j)); + + blockModelSignals(false); + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::valuesRemoved(int index, int count) +{ + if (m_seriesSignalsBlock) + return; + + if (m_count != -1) + m_count -= count; + + blockModelSignals(); + if (m_orientation == Qt::Vertical) + m_model->removeRows(index + m_first, count); + else + m_model->removeColumns(index + m_first, count); + + blockModelSignals(false); + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::boxValueChanged(int index) +{ + if (m_seriesSignalsBlock) + return; + + int boxSetIndex = m_boxSets.indexOf(qobject_cast(QObject::sender())); + + blockModelSignals(); + m_model->setData(boxModelIndex(boxSetIndex + m_firstBoxSetSection, index), m_boxSets.at(boxSetIndex)->at(index)); + blockModelSignals(false); + initializeBoxFromModel(); +} + +void QBoxPlotModelMapperPrivate::initializeBoxFromModel() +{ + if (m_model == 0 || m_series == 0) + return; + + blockSeriesSignals(); + // clear current content + m_series->clear(); + m_boxSets.clear(); + + // create the initial bar sets + for (int i = m_firstBoxSetSection; i <= m_lastBoxSetSection; i++) { + int posInBar = 0; + QModelIndex boxIndex = boxModelIndex(i, posInBar); + // check if there is such model index + if (boxIndex.isValid()) { + QBoxSet *boxSet = new QBoxSet(); + while (boxIndex.isValid()) { + boxSet->append(m_model->data(boxIndex, Qt::DisplayRole).toDouble()); + posInBar++; + boxIndex = boxModelIndex(i, posInBar); + } + connect(boxSet, SIGNAL(valuesAdded(int,int)), this, SLOT(valuesAdded(int,int))); + connect(boxSet, SIGNAL(valuesRemoved(int,int)), this, SLOT(valuesRemoved(int,int))); + connect(boxSet, SIGNAL(valueChanged(int)), this, SLOT(boxValueChanged(int))); + m_series->append(boxSet); + m_boxSets.append(boxSet); + } else { + break; + } + } + blockSeriesSignals(false); +} + +#include "moc_qboxplotmodelmapper.cpp" +#include "moc_qboxplotmodelmapper_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE + diff --git a/src/boxplotchart/qboxplotmodelmapper.h b/src/boxplotchart/qboxplotmodelmapper.h new file mode 100644 index 0000000..8db87a6 --- /dev/null +++ b/src/boxplotchart/qboxplotmodelmapper.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBOXPLOTMODELMAPPER_H +#define QBOXPLOTMODELMAPPER_H + +#include "qchartglobal.h" +#include + +class QAbstractItemModel; + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QBoxPlotModelMapperPrivate; +class QBoxPlotSeries; + +class QTCOMMERCIALCHART_EXPORT QBoxPlotModelMapper : public QObject +{ + Q_OBJECT + +protected: + explicit QBoxPlotModelMapper(QObject *parent = 0); + + QAbstractItemModel *model() const; + void setModel(QAbstractItemModel *model); + + QBoxPlotSeries *series() const; + void setSeries(QBoxPlotSeries *series); + + int first() const; + void setFirst(int first); + + int count() const; + void setCount(int count); + + int firstBoxSetSection() const; + void setFirstBoxSetSection(int firstBoxSetSection); + + int lastBoxSetSection() const; + void setLastBoxSetSection(int lastBoxSetSection); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + +protected: + QBoxPlotModelMapperPrivate * const d_ptr; + Q_DECLARE_PRIVATE(QBoxPlotModelMapper) +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // QBOXPLOTMODELMAPPER_H diff --git a/src/boxplotchart/qboxplotmodelmapper_p.h b/src/boxplotchart/qboxplotmodelmapper_p.h new file mode 100644 index 0000000..bfeeb80 --- /dev/null +++ b/src/boxplotchart/qboxplotmodelmapper_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QBOXPLOTMODELMAPPER_P_H +#define QBOXPLOTMODELMAPPER_P_H + +#include +#include "qboxplotmodelmapper.h" + +class QModelIndex; + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QBoxSet; + +class QBoxPlotModelMapperPrivate : public QObject +{ + Q_OBJECT +public: + explicit QBoxPlotModelMapperPrivate(QBoxPlotModelMapper *q); + +public Q_SLOTS: + // for the model + void modelUpdated(QModelIndex topLeft, QModelIndex bottomRight); + void modelHeaderDataUpdated(Qt::Orientation orientation, int first, int last); + void modelRowsAdded(QModelIndex parent, int start, int end); + void modelRowsRemoved(QModelIndex parent, int start, int end); + void modelColumnsAdded(QModelIndex parent, int start, int end); + void modelColumnsRemoved(QModelIndex parent, int start, int end); + void handleModelDestroyed(); + + // for the series + void boxSetsAdded(QList sets); + void boxSetsRemoved(QList sets); + void valuesAdded(int index, int count); + void valuesRemoved(int index, int count); + void boxValueChanged(int index); + void handleSeriesDestroyed(); + + void initializeBoxFromModel(); + +private: + QBoxSet *boxSet(QModelIndex index); + QModelIndex boxModelIndex(int boxSection, int posInBox); + void insertData(int start, int end); + void removeData(int start, int end); + void blockModelSignals(bool block = true); + void blockSeriesSignals(bool block = true); + +private: + QBoxPlotSeries *m_series; + QList m_boxSets; + QAbstractItemModel *m_model; + int m_first; + int m_count; + Qt::Orientation m_orientation; + int m_firstBoxSetSection; + int m_lastBoxSetSection; + bool m_seriesSignalsBlock; + bool m_modelSignalsBlock; + +private: + QBoxPlotModelMapper *q_ptr; + Q_DECLARE_PUBLIC(QBoxPlotModelMapper) +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // QBOXPLOTMODELMAPPER_P_H diff --git a/src/boxplotchart/qvboxplotmodelmapper.cpp b/src/boxplotchart/qvboxplotmodelmapper.cpp new file mode 100644 index 0000000..750d9be --- /dev/null +++ b/src/boxplotchart/qvboxplotmodelmapper.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvboxplotmodelmapper.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +/*! + \class QVBoxPlotModelMapper + \brief Vertical model mapper for bar series + \mainclass + + Model mappers allow you to use QAbstractItemModel derived models as a data source for a chart series. + Vertical model mapper is used to create a connection between QAbstractBarSeries and QAbstractItemModel derived model object. + Model mapper maintains equal size of all the BarSets. + Adding/removing value from the BarSet causes the the same change in the rest of the BarSets added to the same series. + Note: used model has to support adding/removing rows/columns and modifying the data of the cells. +*/ +/*! + \qmlclass VBarModelMapper + \mainclass + + VBarModelMapper allows you to use your own QAbstractItemModel derived model with data in columns as a data source + for any bar series. It is possible to use both QAbstractItemModel and bar series data API to manipulate data. + VBarModelMapper keeps the series and the model in sync. + + The following QML example would create a bar series with three bar sets (assuming the + model has at least four columns). Each bar set would contain data starting from row 1. The name of a set would be + defined by the horizontal header (of the column). + \code + BarSeries { + VBarModelMapper { + model: myCustomModel // QAbstractItemModel derived implementation + firstBarSetColumn: 1 + lastBarSetColumn: 3 + firstRow: 1 + } + } + \endcode +*/ + +/*! + \property QVBoxPlotModelMapper::series + \brief Defines the QPieSeries object that is used by the mapper. + + All the data in the series is discarded when it is set to the mapper. + When new series is specified the old series is disconnected (it preserves its data) +*/ +/*! + \qmlproperty AbstractBarSeries VBarModelMapper::series + Defines the AbstractBarSeries based object that is used by the mapper. All the data in the series is discarded when it is + set to the mapper. When new series is specified the old series is disconnected (it preserves its data). +*/ + +/*! + \property QVBoxPlotModelMapper::model + \brief Defines the model that is used by the mapper. +*/ +/*! + \qmlproperty SomeModel VBarModelMapper::model + The QAbstractItemModel based model that is used by the mapper. You need to implement the model and expose it to + QML as shown in \l {QML Custom Model} demo application. Note: the model has to support adding/removing rows/columns + and modifying the data of the cells. +*/ + +/*! + \property QVBoxPlotModelMapper::firstBarSetColumn + \brief Defines which column of the model is used as the data source for the first bar set + Default value is: -1 (invalid mapping) +*/ +/*! + \qmlproperty int VBarModelMapper::firstBarSetColumn + Defines which column of the model is used as the data source for the first bar set. Default value + is: -1 (invalid mapping). +*/ + +/*! + \property QVBoxPlotModelMapper::lastBarSetColumn + \brief Defines which column of the model is used as the data source for the last bar set + Default value is: -1 (invalid mapping) +*/ +/*! + \qmlproperty int VBarModelMapper::lastBarSetColumn + Defines which column of the model is used as the data source for the last bar set. Default + value is: -1 (invalid mapping). +*/ + +/*! + \property QVBoxPlotModelMapper::firstRow + \brief Defines which row of the model contains the first values of the QBarSets in the series. + Minimal and default value is: 0 +*/ +/*! + \qmlproperty int VBarModelMapper::firstRow + Defines which row of the model contains the first values of the QBarSets in the series. + The default value is 0. +*/ + +/*! + \property QVBoxPlotModelMapper::rowCount + \brief Defines the number of rows of the model that are mapped as the data for QAbstractBarSeries + Minimal and default value is: -1 (count limited by the number of rows in the model) +*/ +/*! + \qmlproperty int VBarModelMapper::rowCount + Defines the number of rows of the model that are mapped as the data for QAbstractBarSeries. The default value is + -1 (count limited by the number of rows in the model) +*/ + +/*! + \fn void QVBoxPlotModelMapper::seriesReplaced() + + Emitted when the series to which mapper is connected to has changed. +*/ + +/*! + \fn void QVBoxPlotModelMapper::modelReplaced() + + Emitted when the model to which mapper is connected to has changed. +*/ + +/*! + \fn void QVBoxPlotModelMapper::firstBarSetColumnChanged() + Emitted when the firstBarSetColumn has changed. +*/ + +/*! + \fn void QVBoxPlotModelMapper::lastBarSetColumnChanged() + Emitted when the lastBarSetColumn has changed. +*/ + +/*! + \fn void QVBoxPlotModelMapper::firstRowChanged() + Emitted when the firstRow has changed. +*/ + +/*! + \fn void QVBoxPlotModelMapper::rowCountChanged() + Emitted when the rowCount has changed. +*/ + +/*! + Constructs a mapper object which is a child of \a parent. +*/ +QVBoxPlotModelMapper::QVBoxPlotModelMapper(QObject *parent) : + QBoxPlotModelMapper(parent) +{ + QBoxPlotModelMapper::setOrientation(Qt::Vertical); +} + +QAbstractItemModel *QVBoxPlotModelMapper::model() const +{ + return QBoxPlotModelMapper::model(); +} + +void QVBoxPlotModelMapper::setModel(QAbstractItemModel *model) +{ + if (model != QBoxPlotModelMapper::model()) { + QBoxPlotModelMapper::setModel(model); + emit modelReplaced(); + } +} + +QBoxPlotSeries *QVBoxPlotModelMapper::series() const +{ + return QBoxPlotModelMapper::series(); +} + +void QVBoxPlotModelMapper::setSeries(QBoxPlotSeries *series) +{ + if (series != QBoxPlotModelMapper::series()) { + QBoxPlotModelMapper::setSeries(series); + emit seriesReplaced(); + } +} + +int QVBoxPlotModelMapper::firstBoxSetColumn() const +{ + return QBoxPlotModelMapper::firstBoxSetSection(); +} + +void QVBoxPlotModelMapper::setFirstBoxSetColumn(int firstBoxSetColumn) +{ + if (firstBoxSetColumn != firstBoxSetSection()) { + QBoxPlotModelMapper::setFirstBoxSetSection(firstBoxSetColumn); + emit firstBoxSetColumnChanged(); + } +} + +int QVBoxPlotModelMapper::lastBoxSetColumn() const +{ + return QBoxPlotModelMapper::lastBoxSetSection(); +} + +void QVBoxPlotModelMapper::setLastBoxSetColumn(int lastBoxSetColumn) +{ + if (lastBoxSetColumn != lastBoxSetSection()) { + QBoxPlotModelMapper::setLastBoxSetSection(lastBoxSetColumn); + emit lastBoxSetColumnChanged(); + } +} + +int QVBoxPlotModelMapper::firstRow() const +{ + return QBoxPlotModelMapper::first(); +} + +void QVBoxPlotModelMapper::setFirstRow(int firstRow) +{ + if (firstRow != first()) { + QBoxPlotModelMapper::setFirst(firstRow); + emit firstRowChanged(); + } +} + +int QVBoxPlotModelMapper::rowCount() const +{ + return QBoxPlotModelMapper::count(); +} + +void QVBoxPlotModelMapper::setRowCount(int rowCount) +{ + if (rowCount != count()) { + QBoxPlotModelMapper::setCount(rowCount); + emit rowCountChanged(); + } +} + +#include "moc_qvboxplotmodelmapper.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE + diff --git a/src/boxplotchart/qvboxplotmodelmapper.h b/src/boxplotchart/qvboxplotmodelmapper.h new file mode 100644 index 0000000..062750c --- /dev/null +++ b/src/boxplotchart/qvboxplotmodelmapper.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVBOXPLOTMODELMAPPER_H +#define QVBOXPLOTMODELMAPPER_H + +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QTCOMMERCIALCHART_EXPORT QVBoxPlotModelMapper : public QBoxPlotModelMapper +{ + Q_OBJECT + Q_PROPERTY(QBoxPlotSeries *series READ series WRITE setSeries NOTIFY seriesReplaced) + Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelReplaced) + Q_PROPERTY(int firstBoxSetColumn READ firstBoxSetColumn WRITE setFirstBoxSetColumn NOTIFY firstBoxSetColumnChanged) + Q_PROPERTY(int lastBoxSetColumn READ lastBoxSetColumn WRITE setLastBoxSetColumn NOTIFY lastBoxSetColumnChanged) + Q_PROPERTY(int firstRow READ firstRow WRITE setFirstRow NOTIFY firstRowChanged) + Q_PROPERTY(int rowCount READ rowCount WRITE setRowCount NOTIFY rowCountChanged) + +public: + explicit QVBoxPlotModelMapper(QObject *parent = 0); + + QAbstractItemModel *model() const; + void setModel(QAbstractItemModel *model); + + QBoxPlotSeries *series() const; + void setSeries(QBoxPlotSeries *series); + + int firstBoxSetColumn() const; + void setFirstBoxSetColumn(int firstBoxSetColumn); + + int lastBoxSetColumn() const; + void setLastBoxSetColumn(int lastBoxSetColumn); + + int firstRow() const; + void setFirstRow(int firstRow); + + int rowCount() const; + void setRowCount(int rowCount); + +Q_SIGNALS: + void seriesReplaced(); + void modelReplaced(); + void firstBoxSetColumnChanged(); + void lastBoxSetColumnChanged(); + void firstRowChanged(); + void rowCountChanged(); +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // QVBOXPLOTMODELMAPPER_H diff --git a/tests/boxplottester/mainwidget.cpp b/tests/boxplottester/mainwidget.cpp index a8ead32..0300f3f 100644 --- a/tests/boxplottester/mainwidget.cpp +++ b/tests/boxplottester/mainwidget.cpp @@ -20,7 +20,7 @@ #include "mainwidget.h" #include "customtablemodel.h" -#include +#include #include #include #include @@ -214,7 +214,7 @@ void MainWidget::addSeries() m_axis = new QBarCategoryAxis(); m_axis->append(categories); m_chart->createDefaultAxes(); - //m_chart->setAxisX(m_axis, m_series[nSeries]); + m_chart->setAxisX(m_axis, m_series[nSeries]); } nSeries++; @@ -333,12 +333,12 @@ void MainWidget::modelMapperToggled(bool enabled) int first = 0; int count = 5; - QVBarModelMapper *mapper = new QVBarModelMapper(this); - mapper->setFirstBarSetColumn(0); - mapper->setLastBarSetColumn(5); + QVBoxPlotModelMapper *mapper = new QVBoxPlotModelMapper(this); + mapper->setFirstBoxSetColumn(0); + mapper->setLastBoxSetColumn(5); mapper->setFirstRow(first); mapper->setRowCount(count); - //mapper->setSeries(m_series[nSeries]); + mapper->setSeries(m_series[nSeries]); mapper->setModel(m_model); m_chart->addSeries(m_series[nSeries]);