summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@digia.com>2014-05-02 13:02:57 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2014-05-02 14:05:28 +0300
commitdcb83cfdc0e6c8e92df0ca2aacfd34c0ca276e2e (patch)
tree2c344a1c9a5ec8c796a08024216d1dd4219cd2ab
parent13d1117087e66c77d2eea2f0a046fc556c19cb3c (diff)
Optimize single item changes in bar/surface item models.
We are only able to optimize this in cases where rows and columns of the model are directly mapped to rows and columns of the data proxy. In other cases we do not know if the new values of the changed data item in the model actually specify the same row/column in our data proxy as the previous values. Task-number: QTRD-2190 Change-Id: Ie014469ac894474900e5cfd6d91fd1a60353b1f7 Reviewed-by: Titta Heikkala <titta.heikkala@digia.com>
-rw-r--r--src/datavisualization/data/baritemmodelhandler.cpp50
-rw-r--r--src/datavisualization/data/baritemmodelhandler_p.h6
-rw-r--r--src/datavisualization/data/scatteritemmodelhandler.cpp6
-rw-r--r--src/datavisualization/data/surfaceitemmodelhandler.cpp78
-rw-r--r--src/datavisualization/data/surfaceitemmodelhandler_p.h7
-rw-r--r--src/datavisualization/engine/bars3dcontroller.cpp6
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp13
-rw-r--r--tests/itemmodeltest/itemmodeltest.pro11
-rw-r--r--tests/itemmodeltest/main.cpp293
-rw-r--r--tests/tests.pro3
10 files changed, 439 insertions, 34 deletions
diff --git a/src/datavisualization/data/baritemmodelhandler.cpp b/src/datavisualization/data/baritemmodelhandler.cpp
index 4f44fe1d..3b02c1e3 100644
--- a/src/datavisualization/data/baritemmodelhandler.cpp
+++ b/src/datavisualization/data/baritemmodelhandler.cpp
@@ -26,7 +26,9 @@ BarItemModelHandler::BarItemModelHandler(QItemModelBarDataProxy *proxy, QObject
: AbstractItemModelHandler(parent),
m_proxy(proxy),
m_proxyArray(0),
- m_columnCount(0)
+ m_columnCount(0),
+ m_valueRole(noRoleIndex),
+ m_rotationRole(noRoleIndex)
{
}
@@ -34,6 +36,34 @@ BarItemModelHandler::~BarItemModelHandler()
{
}
+void BarItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight,
+ const QVector<int> &roles)
+{
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (!m_proxy->useModelCategories()) {
+ // If the data model doesn't directly map rows and columns, we cannot optimize
+ AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
+ } else {
+ int startRow = qMin(topLeft.row(), bottomRight.row());
+ int endRow = qMax(topLeft.row(), bottomRight.row());
+ int startCol = qMin(topLeft.column(), bottomRight.column());
+ int endCol = qMax(topLeft.column(), bottomRight.column());
+
+ for (int i = startRow; i <= endRow; i++) {
+ for (int j = startCol; j <= endCol; j++) {
+ QBarDataItem item;
+ item.setValue(m_itemModel->index(i, j).data(m_valueRole).toFloat());
+ if (m_rotationRole != noRoleIndex)
+ item.setRotation(m_itemModel->index(i, j).data(m_rotationRole).toFloat());
+ m_proxy->setItem(i, j, item);
+ }
+ }
+ }
+ }
+}
+
// Resolve entire item model into QBarDataArray.
void BarItemModelHandler::resolveModel()
{
@@ -54,8 +84,8 @@ void BarItemModelHandler::resolveModel()
QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
// Default value role to display role if no mapping
- int valueRole = roleHash.key(m_proxy->valueRole().toLatin1(), Qt::DisplayRole);
- int rotationRole = roleHash.key(m_proxy->rotationRole().toLatin1(), noRoleIndex);
+ m_valueRole = roleHash.key(m_proxy->valueRole().toLatin1(), Qt::DisplayRole);
+ m_rotationRole = roleHash.key(m_proxy->rotationRole().toLatin1(), noRoleIndex);
int rowCount = m_itemModel->rowCount();
int columnCount = m_itemModel->columnCount();
@@ -71,9 +101,9 @@ void BarItemModelHandler::resolveModel()
for (int i = 0; i < rowCount; i++) {
QBarDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnCount; j++) {
- newProxyRow[j].setValue(m_itemModel->index(i, j).data(valueRole).toReal());
- if (rotationRole != noRoleIndex)
- newProxyRow[j].setRotation(m_itemModel->index(i, j).data(rotationRole).toReal());
+ newProxyRow[j].setValue(m_itemModel->index(i, j).data(m_valueRole).toFloat());
+ if (m_rotationRole != noRoleIndex)
+ newProxyRow[j].setRotation(m_itemModel->index(i, j).data(m_rotationRole).toFloat());
}
}
// Generate labels from headers if using model rows/columns
@@ -104,9 +134,9 @@ void BarItemModelHandler::resolveModel()
QModelIndex index = m_itemModel->index(i, j);
QString rowRoleStr = index.data(rowRole).toString();
QString columnRoleStr = index.data(columnRole).toString();
- itemValueMap[rowRoleStr][columnRoleStr] = index.data(valueRole).toReal();
- if (rotationRole != noRoleIndex)
- itemRotationMap[rowRoleStr][columnRoleStr] = index.data(rotationRole).toReal();
+ itemValueMap[rowRoleStr][columnRoleStr] = index.data(m_valueRole).toFloat();
+ if (m_rotationRole != noRoleIndex)
+ itemRotationMap[rowRoleStr][columnRoleStr] = index.data(m_rotationRole).toFloat();
if (generateRows && !rowListHash.value(rowRoleStr, false)) {
rowListHash.insert(rowRoleStr, true);
rowList << rowRoleStr;
@@ -142,7 +172,7 @@ void BarItemModelHandler::resolveModel()
QBarDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnList.size(); j++) {
newProxyRow[j].setValue(itemValueMap[rowKey][columnList.at(j)]);
- if (rotationRole != noRoleIndex)
+ if (m_rotationRole != noRoleIndex)
newProxyRow[j].setRotation(itemRotationMap[rowKey][columnList.at(j)]);
}
}
diff --git a/src/datavisualization/data/baritemmodelhandler_p.h b/src/datavisualization/data/baritemmodelhandler_p.h
index 7bf7b0a1..737e0055 100644
--- a/src/datavisualization/data/baritemmodelhandler_p.h
+++ b/src/datavisualization/data/baritemmodelhandler_p.h
@@ -41,12 +41,18 @@ public:
BarItemModelHandler(QItemModelBarDataProxy *proxy, QObject *parent = 0);
virtual ~BarItemModelHandler();
+public slots:
+ virtual void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles = QVector<int> ());
+
protected:
void virtual resolveModel();
QItemModelBarDataProxy *m_proxy; // Not owned
QBarDataArray *m_proxyArray; // Not owned
int m_columnCount;
+ int m_valueRole;
+ int m_rotationRole;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/scatteritemmodelhandler.cpp b/src/datavisualization/data/scatteritemmodelhandler.cpp
index 08ed12f3..280f2a23 100644
--- a/src/datavisualization/data/scatteritemmodelhandler.cpp
+++ b/src/datavisualization/data/scatteritemmodelhandler.cpp
@@ -25,7 +25,11 @@ static const int noRoleIndex = -1;
ScatterItemModelHandler::ScatterItemModelHandler(QItemModelScatterDataProxy *proxy, QObject *parent)
: AbstractItemModelHandler(parent),
m_proxy(proxy),
- m_proxyArray(0)
+ m_proxyArray(0),
+ m_xPosRole(noRoleIndex),
+ m_yPosRole(noRoleIndex),
+ m_zPosRole(noRoleIndex),
+ m_rotationRole(noRoleIndex)
{
}
diff --git a/src/datavisualization/data/surfaceitemmodelhandler.cpp b/src/datavisualization/data/surfaceitemmodelhandler.cpp
index f4383dbf..584ddf28 100644
--- a/src/datavisualization/data/surfaceitemmodelhandler.cpp
+++ b/src/datavisualization/data/surfaceitemmodelhandler.cpp
@@ -25,7 +25,10 @@ static const int noRoleIndex = -1;
SurfaceItemModelHandler::SurfaceItemModelHandler(QItemModelSurfaceDataProxy *proxy, QObject *parent)
: AbstractItemModelHandler(parent),
m_proxy(proxy),
- m_proxyArray(0)
+ m_proxyArray(0),
+ m_xPosRole(noRoleIndex),
+ m_yPosRole(noRoleIndex),
+ m_zPosRole(noRoleIndex)
{
}
@@ -33,6 +36,45 @@ SurfaceItemModelHandler::~SurfaceItemModelHandler()
{
}
+void SurfaceItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight,
+ const QVector<int> &roles)
+{
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (!m_proxy->useModelCategories()) {
+ // If the data model doesn't directly map rows and columns, we cannot optimize
+ AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
+ } else {
+ int startRow = qMin(topLeft.row(), bottomRight.row());
+ int endRow = qMax(topLeft.row(), bottomRight.row());
+ int startCol = qMin(topLeft.column(), bottomRight.column());
+ int endCol = qMax(topLeft.column(), bottomRight.column());
+
+ for (int i = startRow; i <= endRow; i++) {
+ for (int j = startCol; j <= endCol; j++) {
+ QSurfaceDataItem item;
+ const QSurfaceDataItem *oldItem = m_proxy->itemAt(i, j);
+ float xPos;
+ float zPos;
+ if (m_xPosRole != noRoleIndex)
+ xPos = m_itemModel->index(i, j).data(m_xPosRole).toFloat();
+ else
+ xPos = oldItem->x();
+ if (m_zPosRole != noRoleIndex)
+ zPos = m_itemModel->index(i, j).data(m_zPosRole).toFloat();
+ else
+ zPos = oldItem->z();
+ item.setPosition(QVector3D(xPos,
+ m_itemModel->index(i, j).data(m_yPosRole).toFloat(),
+ zPos));
+ m_proxy->setItem(i, j, item);
+ }
+ }
+ }
+ }
+}
+
// Resolve entire item model into QSurfaceDataArray.
void SurfaceItemModelHandler::resolveModel()
{
@@ -52,9 +94,9 @@ void SurfaceItemModelHandler::resolveModel()
QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
// Default to display role if no mapping
- int xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
- int yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), Qt::DisplayRole);
- int zPosRole = roleHash.key(m_proxy->zPosRole().toLatin1(), noRoleIndex);
+ m_xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
+ m_yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), Qt::DisplayRole);
+ m_zPosRole = roleHash.key(m_proxy->zPosRole().toLatin1(), noRoleIndex);
int rowCount = m_itemModel->rowCount();
int columnCount = m_itemModel->columnCount();
@@ -70,10 +112,10 @@ void SurfaceItemModelHandler::resolveModel()
for (int i = 0; i < rowCount; i++) {
QSurfaceDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnCount; j++) {
- float xPos = j;
- float zPos = i;
- if (xPosRole != noRoleIndex) {
- xPos = m_itemModel->index(i, j).data(xPosRole).toFloat();
+ float xPos = float(j);
+ float zPos = float(i);
+ if (m_xPosRole != noRoleIndex) {
+ xPos = m_itemModel->index(i, j).data(m_xPosRole).toFloat();
} else {
QString header = m_itemModel->headerData(j, Qt::Horizontal).toString();
bool ok = false;
@@ -82,8 +124,8 @@ void SurfaceItemModelHandler::resolveModel()
xPos = headerValue;
}
- if (zPosRole != noRoleIndex) {
- zPos = m_itemModel->index(i, j).data(zPosRole).toFloat();
+ if (m_zPosRole != noRoleIndex) {
+ zPos = m_itemModel->index(i, j).data(m_zPosRole).toFloat();
} else {
QString header = m_itemModel->headerData(i, Qt::Vertical).toString();
bool ok = false;
@@ -94,17 +136,17 @@ void SurfaceItemModelHandler::resolveModel()
newProxyRow[j].setPosition(
QVector3D(xPos,
- m_itemModel->index(i, j).data(yPosRole).toFloat(),
+ m_itemModel->index(i, j).data(m_yPosRole).toFloat(),
zPos));
}
}
} else {
int rowRole = roleHash.key(m_proxy->rowRole().toLatin1());
int columnRole = roleHash.key(m_proxy->columnRole().toLatin1());
- if (xPosRole == noRoleIndex)
- xPosRole = columnRole;
- if (zPosRole == noRoleIndex)
- zPosRole = rowRole;
+ if (m_xPosRole == noRoleIndex)
+ m_xPosRole = columnRole;
+ if (m_zPosRole == noRoleIndex)
+ m_zPosRole = rowRole;
bool generateRows = m_proxy->autoRowCategories();
bool generateColumns = m_proxy->autoColumnCategories();
@@ -124,9 +166,9 @@ void SurfaceItemModelHandler::resolveModel()
QModelIndex index = m_itemModel->index(i, j);
QString rowRoleStr = index.data(rowRole).toString();
QString columnRoleStr = index.data(columnRole).toString();
- QVector3D itemPos(index.data(xPosRole).toReal(),
- index.data(yPosRole).toReal(),
- index.data(zPosRole).toReal());
+ QVector3D itemPos(index.data(m_xPosRole).toFloat(),
+ index.data(m_yPosRole).toFloat(),
+ index.data(m_zPosRole).toFloat());
itemValueMap[rowRoleStr][columnRoleStr] = itemPos;
if (generateRows && !rowListHash.value(rowRoleStr, false)) {
rowListHash.insert(rowRoleStr, true);
diff --git a/src/datavisualization/data/surfaceitemmodelhandler_p.h b/src/datavisualization/data/surfaceitemmodelhandler_p.h
index ae426433..dbed0a60 100644
--- a/src/datavisualization/data/surfaceitemmodelhandler_p.h
+++ b/src/datavisualization/data/surfaceitemmodelhandler_p.h
@@ -41,11 +41,18 @@ public:
SurfaceItemModelHandler(QItemModelSurfaceDataProxy *proxy, QObject *parent = 0);
virtual ~SurfaceItemModelHandler();
+public slots:
+ virtual void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles = QVector<int> ());
+
protected:
void virtual resolveModel();
QItemModelSurfaceDataProxy *m_proxy; // Not owned
QSurfaceDataArray *m_proxyArray; // Not owned
+ int m_xPosRole;
+ int m_yPosRole;
+ int m_zPosRole;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp
index 99fa8223..35b24218 100644
--- a/src/datavisualization/engine/bars3dcontroller.cpp
+++ b/src/datavisualization/engine/bars3dcontroller.cpp
@@ -148,7 +148,6 @@ void Bars3DController::handleRowsChanged(int startIndex, int count)
if (!oldChangeCount)
m_changedRows.reserve(count);
- int selectedRow = m_selectedBar.x();
for (int i = 0; i < count; i++) {
bool newItem = true;
int candidate = startIndex + i;
@@ -162,7 +161,7 @@ void Bars3DController::handleRowsChanged(int startIndex, int count)
if (newItem) {
ChangeRow newChangeItem = {series, candidate};
m_changedRows.append(newChangeItem);
- if (series == m_selectedBarSeries && selectedRow == candidate)
+ if (series == m_selectedBarSeries && m_selectedBar.x() == candidate)
series->d_ptr->markItemLabelDirty();
}
}
@@ -516,7 +515,8 @@ void Bars3DController::setSelectedBar(const QPoint &position, QBar3DSeries *seri
adjustSelectionPosition(pos, series);
if (selectionMode().testFlag(QAbstract3DGraph::SelectionSlice)) {
- // If the selected bar is outside data window, or there is no visible selected bar, disable slicing
+ // If the selected bar is outside data window, or there is no visible selected bar,
+ // disable slicing.
if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max()
|| pos.y() < m_axisX->min() || pos.y() > m_axisX->max()
|| !series->isVisible()) {
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index ab7bb4ca..f93d20b6 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -337,8 +337,14 @@ void Bars3DRenderer::updateRows(const QVector<Bars3DController::ChangeRow> &rows
if (!cache->isVisible() && !cache->dataDirty())
cache->setDataDirty(true);
}
- if (cache->isVisible())
+ if (cache->isVisible()) {
updateRenderRow(dataArray->at(row), cache->renderArray()[row - minRow]);
+ if (m_cachedIsSlicingActivated
+ && cache == m_selectedSeriesCache
+ && m_selectedBarPos.x() == row) {
+ m_selectionDirty = true; // Need to update slice view
+ }
+ }
}
}
@@ -370,6 +376,11 @@ void Bars3DRenderer::updateItems(const QVector<Bars3DController::ChangeItem> &it
if (cache->isVisible()) {
updateRenderItem(dataArray->at(row)->at(col),
cache->renderArray()[row - minRow][col - minCol]);
+ if (m_cachedIsSlicingActivated
+ && cache == m_selectedSeriesCache
+ && m_selectedBarPos == QPoint(row, col)) {
+ m_selectionDirty = true; // Need to update slice view
+ }
}
}
}
diff --git a/tests/itemmodeltest/itemmodeltest.pro b/tests/itemmodeltest/itemmodeltest.pro
new file mode 100644
index 00000000..d1cf0959
--- /dev/null
+++ b/tests/itemmodeltest/itemmodeltest.pro
@@ -0,0 +1,11 @@
+android|ios {
+ error( "This test is not supported for android or ios." )
+}
+
+!include( ../tests.pri ) {
+ error( "Couldn't find the tests.pri file!" )
+}
+
+SOURCES += main.cpp
+
+QT += widgets
diff --git a/tests/itemmodeltest/main.cpp b/tests/itemmodeltest/main.cpp
new file mode 100644
index 00000000..419ee162
--- /dev/null
+++ b/tests/itemmodeltest/main.cpp
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise 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
+**
+****************************************************************************/
+
+#include <QtDataVisualization/q3dbars.h>
+#include <QtDataVisualization/q3dsurface.h>
+#include <QtDataVisualization/qcategory3daxis.h>
+#include <QtDataVisualization/qitemmodelbardataproxy.h>
+#include <QtDataVisualization/qitemmodelsurfacedataproxy.h>
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/qbar3dseries.h>
+#include <QtDataVisualization/q3dtheme.h>
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QTableWidget>
+#include <QtGui/QScreen>
+#include <QtCore/QTimer>
+#include <QtCore/QDebug>
+#include <QtWidgets/QHeaderView>
+#include <QtWidgets/QPushButton>
+
+#define USE_STATIC_DATA
+
+using namespace QtDataVisualization;
+
+class GraphDataGenerator : public QObject
+{
+public:
+ explicit GraphDataGenerator(Q3DBars *bargraph, Q3DSurface * surfaceGraph,
+ QTableWidget *tableWidget);
+ ~GraphDataGenerator();
+
+ void setupModel();
+ void addRow();
+ void changeStyle();
+ void changePresetCamera();
+ void changeTheme();
+ void start();
+ void selectFromTable(const QPoint &selection);
+ void selectedFromTable(int currentRow, int currentColumn, int previousRow, int previousColumn);
+ void fixTableSize();
+ void changeSelectedButtonClicked();
+
+private:
+ Q3DBars *m_barGraph;
+ Q3DSurface *m_surfaceGraph;
+ QTimer *m_dataTimer;
+ QTimer *m_styleTimer;
+ QTimer *m_presetTimer;
+ QTimer *m_themeTimer;
+ int m_columnCount;
+ int m_rowCount;
+ QTableWidget *m_tableWidget; // not owned
+};
+
+GraphDataGenerator::GraphDataGenerator(Q3DBars *bargraph, Q3DSurface * surfaceGraph,
+ QTableWidget *tableWidget)
+ : m_barGraph(bargraph),
+ m_surfaceGraph(surfaceGraph),
+ m_dataTimer(0),
+ m_styleTimer(0),
+ m_presetTimer(0),
+ m_themeTimer(0),
+ m_columnCount(100),
+ m_rowCount(50),
+ m_tableWidget(tableWidget)
+{
+ // Set up bar specifications; make the bars as wide as they are deep,
+ // and add a small space between them
+ m_barGraph->setBarThickness(1.0f);
+ m_barGraph->setBarSpacing(QSizeF(0.2, 0.2));
+
+#ifndef USE_STATIC_DATA
+ // Set up sample space; make it as deep as it's wide
+ m_barGraph->rowAxis()->setRange(0, m_rowCount);
+ m_barGraph->columnAxis()->setRange(0, m_columnCount);
+ m_tableWidget->setColumnCount(m_columnCount);
+
+ // Set selection mode to full
+ m_barGraph->setSelectionMode(QAbstract3DGraph::SelectionItemRowAndColumn);
+
+ // Hide axis labels by explicitly setting one empty string as label list
+ m_barGraph->rowAxis()->setLabels(QStringList(QString()));
+ m_barGraph->columnAxis()->setLabels(QStringList(QString()));
+
+ m_barGraph->seriesList().at(0)->setItemLabelFormat(QStringLiteral("@valueLabel"));
+#else
+ // Set selection mode to slice row
+ m_barGraph->setSelectionMode(
+ QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice);
+ m_surfaceGraph->setSelectionMode(
+ QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice);
+#endif
+}
+
+GraphDataGenerator::~GraphDataGenerator()
+{
+ if (m_dataTimer) {
+ m_dataTimer->stop();
+ delete m_dataTimer;
+ }
+ delete m_barGraph;
+ delete m_surfaceGraph;
+}
+
+void GraphDataGenerator::start()
+{
+#ifndef USE_STATIC_DATA
+ m_dataTimer = new QTimer();
+ m_dataTimer->setTimerType(Qt::CoarseTimer);
+ QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::addRow);
+ m_dataTimer->start(0);
+ m_tableWidget->setFixedWidth(m_graph->width());
+#else
+ setupModel();
+
+ // Table needs to be shown before the size of its headers can be accurately obtained,
+ // so we postpone it a bit
+ m_dataTimer = new QTimer();
+ m_dataTimer->setSingleShot(true);
+ QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::fixTableSize);
+ m_dataTimer->start(0);
+#endif
+}
+
+void GraphDataGenerator::setupModel()
+{
+ // Set up row and column names
+ QStringList days;
+ days << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday" << "Saturday" << "Sunday";
+ QStringList weeks;
+ weeks << "week 1" << "week 2" << "week 3" << "week 4" << "week 5";
+
+ // Set up data Mon Tue Wed Thu Fri Sat Sun
+ float hours[5][7] = {{2.0f, 1.0f, 3.0f, 0.2f, 1.0f, 5.0f, 10.0f}, // week 1
+ {0.5f, 1.0f, 3.0f, 1.0f, 2.0f, 2.0f, 3.0f}, // week 2
+ {1.0f, 1.0f, 2.0f, 1.0f, 4.0f, 4.0f, 4.0f}, // week 3
+ {0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, 0.3f}, // week 4
+ {3.0f, 3.0f, 6.0f, 2.0f, 2.0f, 1.0f, 1.0f}}; // week 5
+
+ // Add labels
+ m_barGraph->rowAxis()->setTitle("Week of year");
+ m_barGraph->columnAxis()->setTitle("Day of week");
+ m_barGraph->valueAxis()->setTitle("Hours spent on the Internet");
+ m_barGraph->valueAxis()->setLabelFormat("%.1f h");
+
+ m_surfaceGraph->axisZ()->setTitle("Week of year");
+ m_surfaceGraph->axisX()->setTitle("Day of week");
+ m_surfaceGraph->axisY()->setTitle("Hours spent on the Internet");
+ m_surfaceGraph->axisY()->setLabelFormat("%.1f h");
+
+ m_tableWidget->setRowCount(5);
+ m_tableWidget->setColumnCount(7);
+ m_tableWidget->setHorizontalHeaderLabels(days);
+ m_tableWidget->setVerticalHeaderLabels(weeks);
+ m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_tableWidget->setCurrentCell(-1, -1);
+
+ for (int week = 0; week < weeks.size(); week++) {
+ for (int day = 0; day < days.size(); day++) {
+ QModelIndex index = m_tableWidget->model()->index(week, day);
+ m_tableWidget->model()->setData(index, hours[week][day]);
+ }
+ }
+}
+
+void GraphDataGenerator::addRow()
+{
+ m_tableWidget->model()->insertRow(0);
+ if (m_tableWidget->model()->rowCount() > m_rowCount)
+ m_tableWidget->model()->removeRow(m_rowCount);
+ for (int i = 0; i < m_columnCount; i++) {
+ QModelIndex index = m_tableWidget->model()->index(0, i);
+ m_tableWidget->model()->setData(index,
+ ((float)i / (float)m_columnCount) / 2.0f + (float)(rand() % 30) / 100.0f);
+ }
+ m_tableWidget->resizeColumnsToContents();
+}
+
+void GraphDataGenerator::selectFromTable(const QPoint &selection)
+{
+ m_tableWidget->setFocus();
+ m_tableWidget->setCurrentCell(selection.x(), selection.y());
+}
+
+void GraphDataGenerator::selectedFromTable(int currentRow, int currentColumn,
+ int previousRow, int previousColumn)
+{
+ Q_UNUSED(previousRow)
+ Q_UNUSED(previousColumn)
+ m_barGraph->seriesList().at(0)->setSelectedBar(QPoint(currentRow, currentColumn));
+ m_surfaceGraph->seriesList().at(0)->setSelectedPoint(QPoint(currentRow, currentColumn));
+}
+
+void GraphDataGenerator::fixTableSize()
+{
+ int width = m_tableWidget->horizontalHeader()->length();
+ width += m_tableWidget->verticalHeader()->width();
+ m_tableWidget->setFixedWidth(width + 2);
+ int height = m_tableWidget->verticalHeader()->length();
+ height += m_tableWidget->horizontalHeader()->height();
+ m_tableWidget->setFixedHeight(height + 2);
+}
+
+void GraphDataGenerator::changeSelectedButtonClicked()
+{
+ // Change all selected cells to a random value 1-10
+ QVariant value = QVariant::fromValue(float((rand() % 10) + 1));
+ QList<QTableWidgetItem *> selectedItems = m_tableWidget->selectedItems();
+ foreach (QTableWidgetItem *item, selectedItems)
+ item->setData(Qt::DisplayRole, value);
+}
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DBars *barGraph = new Q3DBars();
+ Q3DSurface *surfaceGraph = new Q3DSurface();
+ QWidget *barContainer = QWidget::createWindowContainer(barGraph);
+ QWidget *surfaceContainer = QWidget::createWindowContainer(surfaceGraph);
+
+ barContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ barContainer->setFocusPolicy(Qt::StrongFocus);
+ surfaceContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ surfaceContainer->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget widget;
+ QVBoxLayout *mainLayout = new QVBoxLayout(&widget);
+ QHBoxLayout *graphLayout = new QHBoxLayout();
+ QVBoxLayout *buttonLayout = new QVBoxLayout();
+ QHBoxLayout *bottomLayout = new QHBoxLayout();
+ QTableWidget *tableWidget = new QTableWidget(&widget);
+ QPushButton *changeSelectedButton = new QPushButton(&widget);
+ changeSelectedButton->setText(QStringLiteral("Change Selected"));
+
+ buttonLayout->addWidget(changeSelectedButton);
+ graphLayout->addWidget(barContainer);
+ graphLayout->addWidget(surfaceContainer);
+ bottomLayout->addLayout(buttonLayout);
+ bottomLayout->addWidget(tableWidget);
+ mainLayout->addLayout(graphLayout);
+ mainLayout->addLayout(bottomLayout);
+
+ tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ tableWidget->setAlternatingRowColors(true);
+ widget.setWindowTitle(QStringLiteral("Hours spent on the Internet"));
+
+ // Since we are dealing with QTableWidget, the model will already have data sorted properly
+ // into rows and columns, so we simply set useModelCategories property to true to utilize this.
+ QItemModelBarDataProxy *barProxy = new QItemModelBarDataProxy(tableWidget->model());
+ QItemModelSurfaceDataProxy *surfaceProxy = new QItemModelSurfaceDataProxy(tableWidget->model());
+ barProxy->setUseModelCategories(true);
+ surfaceProxy->setUseModelCategories(true);
+ QBar3DSeries *barSeries = new QBar3DSeries(barProxy);
+ QSurface3DSeries *surfaceSeries = new QSurface3DSeries(surfaceProxy);
+ barSeries->setMesh(QAbstract3DSeries::MeshPyramid);
+ barGraph->addSeries(barSeries);
+ surfaceGraph->addSeries(surfaceSeries);
+
+ GraphDataGenerator generator(barGraph, surfaceGraph, tableWidget);
+ QObject::connect(barSeries, &QBar3DSeries::selectedBarChanged, &generator,
+ &GraphDataGenerator::selectFromTable);
+ QObject::connect(surfaceSeries, &QSurface3DSeries::selectedPointChanged, &generator,
+ &GraphDataGenerator::selectFromTable);
+ QObject::connect(tableWidget, &QTableWidget::currentCellChanged, &generator,
+ &GraphDataGenerator::selectedFromTable);
+ QObject::connect(changeSelectedButton, &QPushButton::clicked, &generator,
+ &GraphDataGenerator::changeSelectedButtonClicked);
+
+ QSize screenSize = barGraph->screen()->size();
+ widget.resize(QSize(screenSize.width() / 2, screenSize.height() / 2));
+ widget.show();
+ generator.start();
+ return app.exec();
+}
diff --git a/tests/tests.pro b/tests/tests.pro
index 613534e9..7b690c7d 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -14,7 +14,8 @@ SUBDIRS += barstest \
qmldynamicdata \
multigraphs \
directional \
- qmlmultiwindow
+ qmlmultiwindow \
+ itemmodeltest
#SUBDIRS += kinectsurface