diff --git a/src/animations/boxplotanimation.cpp b/src/animations/boxplotanimation.cpp index f645f67..d7e4f4d 100644 --- a/src/animations/boxplotanimation.cpp +++ b/src/animations/boxplotanimation.cpp @@ -53,7 +53,25 @@ void BoxPlotAnimation::addBox(BoxWhiskers *box) ChartAnimation *BoxPlotAnimation::boxAnimation(BoxWhiskers *box) { // TODO: Check for missing animation - return m_animations.value(box); + BoxWhiskersAnimation *animation = m_animations.value(box); + animation->m_moveMedianLine = false; + + return animation; +} + +ChartAnimation *BoxPlotAnimation::boxChangeAnimation(BoxWhiskers *box) +{ + BoxWhiskersAnimation *animation = m_animations.value(box); + animation->m_moveMedianLine = true; + animation->setEndData(box->m_data); + + return animation; +} + +void BoxPlotAnimation::setAnimationStart(BoxWhiskers *box) +{ + BoxWhiskersAnimation *animation = m_animations.value(box); + animation->setStartData(box->m_data); } //#include "moc_boxplotanimation_p.cpp" diff --git a/src/animations/boxplotanimation_p.h b/src/animations/boxplotanimation_p.h index f4d95e9..d59d2f9 100644 --- a/src/animations/boxplotanimation_p.h +++ b/src/animations/boxplotanimation_p.h @@ -48,6 +48,9 @@ public: void addBox(BoxWhiskers *box); ChartAnimation *boxAnimation(BoxWhiskers *box); + ChartAnimation *boxChangeAnimation(BoxWhiskers *box); + + void setAnimationStart(BoxWhiskers *box); protected: BoxPlotChartItem *m_item; diff --git a/src/animations/boxwhiskersanimation.cpp b/src/animations/boxwhiskersanimation.cpp index d577082..544256c 100644 --- a/src/animations/boxwhiskersanimation.cpp +++ b/src/animations/boxwhiskersanimation.cpp @@ -30,14 +30,6 @@ Q_DECLARE_METATYPE(qreal) QTCOMMERCIALCHART_BEGIN_NAMESPACE -BoxWhiskersAnimation::BoxWhiskersAnimation(BoxPlotChartItem *item) - : ChartAnimation(item), - m_item(item) -{ - setDuration(ChartAnimationDuration); - setEasingCurve(QEasingCurve::OutQuart); -} - BoxWhiskersAnimation::BoxWhiskersAnimation(BoxWhiskers *box) : ChartAnimation(box), m_box(box) @@ -61,21 +53,22 @@ QVariant BoxWhiskersAnimation::interpolated(const QVariant &from, const QVariant //qDebug() << "endData.m_median = " << endData.m_median; } - result.m_lowerExtreme = endData.m_median + progress * (endData.m_lowerExtreme - endData.m_median); - result.m_lowerQuartile = endData.m_median + progress * (endData.m_lowerQuartile - endData.m_median); - result.m_median = endData.m_median; - result.m_upperQuartile = endData.m_median + progress * (endData.m_upperQuartile - endData.m_median); - result.m_upperExtreme = endData.m_median + progress * (endData.m_upperExtreme - endData.m_median); + if (m_moveMedianLine) { + result.m_lowerExtreme = startData.m_lowerExtreme + progress * (endData.m_lowerExtreme - startData.m_lowerExtreme); + result.m_lowerQuartile = startData.m_lowerQuartile + progress * (endData.m_lowerQuartile - startData.m_lowerQuartile); + result.m_median = startData.m_median + progress * (endData.m_median - startData.m_median); + result.m_upperQuartile = startData.m_upperQuartile + progress * (endData.m_upperQuartile - startData.m_upperQuartile); + result.m_upperExtreme = startData.m_upperExtreme + progress * (endData.m_upperExtreme - startData.m_upperExtreme); + } else { + result.m_lowerExtreme = endData.m_median + progress * (endData.m_lowerExtreme - endData.m_median); + result.m_lowerQuartile = endData.m_median + progress * (endData.m_lowerQuartile - endData.m_median); + result.m_median = endData.m_median; + result.m_upperQuartile = endData.m_median + progress * (endData.m_upperQuartile - endData.m_median); + result.m_upperExtreme = endData.m_median + progress * (endData.m_upperExtreme - endData.m_median); + } result.m_index = endData.m_index; result.m_boxItems = endData.m_boxItems; -// result.m_lowerExtreme = endData.m_lowerExtreme; -// result.m_lowerQuartile = endData.m_lowerQuartile; -// result.m_median = endData.m_median; -// result.m_upperQuartile = endData.m_upperQuartile; -// result.m_upperExtreme = endData.m_upperExtreme; -// result.m_index = endData.m_index; -// result.m_boxItems = endData.m_boxItems; result.m_maxX = endData.m_maxX; result.m_minX = endData.m_minX; @@ -107,6 +100,15 @@ void BoxWhiskersAnimation::setEndData(const BoxWhiskersData &endData) setEndValue(qVariantFromValue(endData)); } +void BoxWhiskersAnimation::setStartData(const BoxWhiskersData &endData) +{ + if (state() != QAbstractAnimation::Stopped) + stop(); + + setStartValue(qVariantFromValue(endData)); +} + + #include "moc_boxwhiskersanimation_p.cpp" QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/animations/boxwhiskersanimation_p.h b/src/animations/boxwhiskersanimation_p.h index 3b8979f..ad37743 100644 --- a/src/animations/boxwhiskersanimation_p.h +++ b/src/animations/boxwhiskersanimation_p.h @@ -43,7 +43,6 @@ class BoxWhiskersAnimation : public ChartAnimation Q_OBJECT public: - BoxWhiskersAnimation(BoxPlotChartItem *item); BoxWhiskersAnimation(BoxWhiskers *box); ~BoxWhiskersAnimation(); @@ -53,11 +52,16 @@ public: // from QVariantAnimation void setup(const BoxWhiskersData &startData, const BoxWhiskersData &endData); void setEndData(const BoxWhiskersData &endData); + void setStartData(const BoxWhiskersData &endData); + + void moveMedianLine(bool move); protected: + friend class BoxPlotAnimation; BoxPlotChartItem *m_item; BoxWhiskers *m_box; BoxWhiskersData *m_boxData; + bool m_moveMedianLine; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/boxplotchart/boxplotchartitem.cpp b/src/boxplotchart/boxplotchartitem.cpp index 03f1f40..9af4e0b 100644 --- a/src/boxplotchart/boxplotchartitem.cpp +++ b/src/boxplotchart/boxplotchartitem.cpp @@ -136,22 +136,16 @@ void BoxPlotChartItem::handleDomainUpdated() void BoxPlotChartItem::handleLayoutChanged() { - //qDebug() << "BoxPlotChartItem::handleLayoutChanged() domain()->size() = " << domain()->size(); - //foreach (BoxWhiskers *boxWhiskersItem, m_boxes) - // boxWhiskersItem->updateGeometry(); -} + foreach (BoxWhiskers *item, m_boxTable.values()) { + if (m_animation) + m_animation->setAnimationStart(item); -void BoxPlotChartItem::handleSeriesRemove(QAbstractSeries *series) -{ - qDebug() << "BoxPlotChartItem::handleSeriesRemove " << m_seriesIndex; - QBoxPlotSeries *removedSeries = static_cast(series); - if (m_series == removedSeries) { - return; + bool dirty = updateBoxGeometry(item, item->m_data.m_index); + if (dirty && m_animation) + presenter()->startAnimation(m_animation->boxChangeAnimation(item)); + else + item->updateGeometry(); } - - m_seriesCount = m_seriesCount - 1; - - handleDataStructureChanged(); } QRectF BoxPlotChartItem::boundingRect() const @@ -169,11 +163,17 @@ QVector BoxPlotChartItem::calculateLayout() return QVector(); } -void BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index) +bool BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index) { + bool changed = false; + QBarSet *set = m_series->d_func()->barsetAt(index); - //QBarSet *set = m_barSets.at(index); BoxWhiskersData &data = box->m_data; + + if ((data.m_lowerExtreme != set->at(0)) || (data.m_lowerQuartile != set->at(1)) || + (data.m_median != set->at(2)) || (data.m_upperQuartile != set->at(3)) || (data.m_upperExtreme != set->at(4))) + changed = true; + data.m_lowerExtreme = set->at(0); data.m_lowerQuartile = set->at(1); data.m_median = set->at(2); @@ -189,6 +189,8 @@ void BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index) data.m_seriesIndex = m_seriesIndex; data.m_seriesCount = m_seriesCount; + + return changed; } #include "moc_boxplotchartitem_p.cpp" diff --git a/src/boxplotchart/boxplotchartitem_p.h b/src/boxplotchart/boxplotchartitem_p.h index 40813f4..342221e 100644 --- a/src/boxplotchart/boxplotchartitem_p.h +++ b/src/boxplotchart/boxplotchartitem_p.h @@ -58,12 +58,11 @@ public Q_SLOTS: void handleDomainUpdated(); void handleLayoutChanged(); void handleUpdatedBars(); - void handleSeriesRemove(QAbstractSeries *series); private: virtual QVector calculateLayout(); void initializeLayout(); - void updateBoxGeometry(BoxWhiskers *box, int index); + bool updateBoxGeometry(BoxWhiskers *box, int index); protected: friend class QBoxPlotSeriesPrivate; diff --git a/tests/boxplottester/boxplottester.pro b/tests/boxplottester/boxplottester.pro index 822f78d..57436c0 100644 --- a/tests/boxplottester/boxplottester.pro +++ b/tests/boxplottester/boxplottester.pro @@ -10,7 +10,9 @@ contains(QT_MAJOR_VERSION, 5) { } SOURCES += main.cpp \ - mainwidget.cpp + mainwidget.cpp \ + customtablemodel.cpp HEADERS += \ - mainwidget.h + mainwidget.h \ + customtablemodel.h diff --git a/tests/boxplottester/customtablemodel.cpp b/tests/boxplottester/customtablemodel.cpp new file mode 100644 index 0000000..54d4790 --- /dev/null +++ b/tests/boxplottester/customtablemodel.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** 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 "customtablemodel.h" +#include +#include +#include +#include + +const QString categories[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Nov", "Dec"}; + +CustomTableModel::CustomTableModel(QObject *parent) : + QAbstractTableModel(parent) +{ +// qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); + + m_columnCount = 6; + m_rowCount = 5; + QVector* dataVec_Jan = new QVector(m_rowCount); + dataVec_Jan->insert(0, 3.0); + dataVec_Jan->insert(1, 4.0); + dataVec_Jan->insert(2, 4.4); + dataVec_Jan->insert(3, 6.0); + dataVec_Jan->insert(4, 7.0); + m_data.append(dataVec_Jan); + + QVector* dataVec_Feb = new QVector(m_rowCount); + dataVec_Feb->insert(0, 5.0); + dataVec_Feb->insert(1, 6.0); + dataVec_Feb->insert(2, 7.5); + dataVec_Feb->insert(3, 8.0); + dataVec_Feb->insert(4, 12.0); + m_data.append(dataVec_Feb); + + QVector* dataVec_Mar = new QVector(m_rowCount); + dataVec_Mar->insert(0, 3.0); + dataVec_Mar->insert(1, 4.0); + dataVec_Mar->insert(2, 5.7); + dataVec_Mar->insert(3, 8.0); + dataVec_Mar->insert(4, 9.0); + m_data.append(dataVec_Mar); + + QVector* dataVec_Apr = new QVector(m_rowCount); + dataVec_Apr->insert(0, 5.0); + dataVec_Apr->insert(1, 6.0); + dataVec_Apr->insert(2, 6.8); + dataVec_Apr->insert(3, 7.0); + dataVec_Apr->insert(4, 8.0); + m_data.append(dataVec_Apr); + + QVector* dataVec_May = new QVector(m_rowCount); + dataVec_May->insert(0, 4.0); + dataVec_May->insert(1, 5.0); + dataVec_May->insert(2, 5.2); + dataVec_May->insert(3, 6.0); + dataVec_May->insert(4, 7.0); + m_data.append(dataVec_May); + + QVector* dataVec_Jun = new QVector(m_rowCount); + dataVec_Jun->insert(0, 4.0); + dataVec_Jun->insert(1, 7.0); + dataVec_Jun->insert(2, 8.2); + dataVec_Jun->insert(3, 9.0); + dataVec_Jun->insert(4, 10.0); + m_data.append(dataVec_Jun); +} + +int CustomTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_rowCount; +} + +int CustomTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_data.count(); +} + +QVariant CustomTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return categories[section]; + else + return QString("%1").arg(section + 1); +} + +QVariant CustomTableModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + return m_data[index.column()]->at(index.row()); + } else if (role == Qt::EditRole) { + return m_data[index.column()]->at(index.row()); + } else if (role == Qt::BackgroundRole) { + QRect rect; + foreach (rect, m_mapping) + if (rect.contains(index.column(), index.row())) + return QColor(m_mapping.key(rect)); + + // cell not mapped return white color + return QColor(Qt::white); + } + return QVariant(); +} + +bool CustomTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) { + m_data[index.column()]->replace(index.row(), value.toDouble()); + emit dataChanged(index, index); + return true; + } + return false; +} + +Qt::ItemFlags CustomTableModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +void CustomTableModel::addMapping(QString color, QRect area) +{ + m_mapping.insertMulti(color, area); +} diff --git a/tests/boxplottester/customtablemodel.h b/tests/boxplottester/customtablemodel.h new file mode 100644 index 0000000..47d60fd --- /dev/null +++ b/tests/boxplottester/customtablemodel.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** 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 CUSTOMTABLEMODEL_H +#define CUSTOMTABLEMODEL_H + +#include +#include +#include + +class CustomTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit CustomTableModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + + void addMapping(QString color, QRect area); + void clearMapping() { m_mapping.clear(); } + +private: + QList * > m_data; + QHash m_mapping; + int m_columnCount; + int m_rowCount; +}; + +#endif // CUSTOMTABLEMODEL_H diff --git a/tests/boxplottester/mainwidget.cpp b/tests/boxplottester/mainwidget.cpp index fe5303d..eb3d3b1 100644 --- a/tests/boxplottester/mainwidget.cpp +++ b/tests/boxplottester/mainwidget.cpp @@ -19,6 +19,10 @@ ****************************************************************************/ #include "mainwidget.h" +#include "customtablemodel.h" +#include +#include +#include #include #include #include @@ -76,6 +80,19 @@ MainWidget::MainWidget(QWidget *parent) : initThemeCombo(grid); initCheckboxes(grid); + m_model = new CustomTableModel; + QTableView *tableView = new QTableView; + tableView->setModel(m_model); + tableView->setMaximumWidth(200); + grid->addWidget(tableView, rowPos++, 0, 3, 2, Qt::AlignLeft); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); +#else + tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + tableView->verticalHeader()->setResizeMode(QHeaderView::Stretch); +#endif + // add row with empty label to make all the other rows static grid->addWidget(new QLabel(""), grid->rowCount(), 0); grid->setRowStretch(grid->rowCount() - 1, 1); @@ -129,6 +146,12 @@ void MainWidget::initCheckboxes(QGridLayout *grid) connect(titleCheckBox, SIGNAL(toggled(bool)), this, SLOT(titleToggled(bool))); titleCheckBox->setChecked(false); grid->addWidget(titleCheckBox, rowPos++, 0); + + QCheckBox *modelMapperCheckBox = new QCheckBox("Use model mapper"); + connect(modelMapperCheckBox, SIGNAL(toggled(bool)), this, SLOT(modelMapperToggled(bool))); + modelMapperCheckBox->setChecked(false); + grid->addWidget(modelMapperCheckBox, rowPos++, 0); + } void MainWidget::addSeries() @@ -228,6 +251,27 @@ void MainWidget::titleToggled(bool enabled) m_chart->setTitle(""); } +void MainWidget::modelMapperToggled(bool enabled) +{ + if (enabled) { + m_series[nSeries] = new QBoxPlotSeries(); + + int first = 0; + int count = 5; + QVBarModelMapper *mapper = new QVBarModelMapper(this); + mapper->setFirstBarSetColumn(0); + mapper->setLastBarSetColumn(5); + mapper->setFirstRow(first); + mapper->setRowCount(count); + mapper->setSeries(m_series[nSeries]); + mapper->setModel(m_model); + m_chart->addSeries(m_series[nSeries]); + + nSeries++; + } else { + removeSeries(); + } +} void MainWidget::changeChartTheme(int themeIndex) { diff --git a/tests/boxplottester/mainwidget.h b/tests/boxplottester/mainwidget.h index 5e20c83..dc57991 100644 --- a/tests/boxplottester/mainwidget.h +++ b/tests/boxplottester/mainwidget.h @@ -24,6 +24,7 @@ #include "qchartglobal.h" #include "qchart.h" #include "qchartview.h" +#include "customtablemodel.h" #include #include #include @@ -51,6 +52,7 @@ private slots: void animationToggled(bool enabled); void legendToggled(bool enabled); void titleToggled(bool enabled); + void modelMapperToggled(bool enabled); void changeChartTheme(int themeIndex); private: @@ -58,6 +60,7 @@ private: QChartView *m_chartView; QGridLayout *m_scatterLayout; QBarCategoryAxis *m_axis; + CustomTableModel *m_model; int rowPos; int nSeries; int nNewBoxes;