summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@digia.com>2014-05-13 12:37:06 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2014-05-13 12:57:45 +0300
commit576050ff96cbf67014313bd7c1f2b475b00dd80c (patch)
tree01e9f6d2abaef6e535d8433ed23821d77cac10ac
parent200031404f12422b78e9220aeb4fa12ba8f358a7 (diff)
Multi-match behavior implementation for bar item model proxy
Task-number: QTRD-3074 Change-Id: I8e34d2546198a743e0132f0ce201dd38daf7ce7a Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
-rw-r--r--src/datavisualization/data/baritemmodelhandler.cpp49
-rw-r--r--src/datavisualization/data/qitemmodelbardataproxy.cpp61
-rw-r--r--src/datavisualization/data/qitemmodelbardataproxy.h13
-rw-r--r--src/datavisualization/data/qitemmodelbardataproxy_p.h2
-rw-r--r--tests/qmlmultitest/qml/qmlmultitest/Data.qml21
-rw-r--r--tests/qmlmultitest/qml/qmlmultitest/main.qml32
6 files changed, 159 insertions, 19 deletions
diff --git a/src/datavisualization/data/baritemmodelhandler.cpp b/src/datavisualization/data/baritemmodelhandler.cpp
index 4685be44..3d1ce82f 100644
--- a/src/datavisualization/data/baritemmodelhandler.cpp
+++ b/src/datavisualization/data/baritemmodelhandler.cpp
@@ -17,6 +17,7 @@
****************************************************************************/
#include "baritemmodelhandler_p.h"
+#include <QTime>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -176,8 +177,17 @@ void BarItemModelHandler::resolveModel()
// Sort values into rows and columns
typedef QHash<QString, float> ColumnValueMap;
- QHash <QString, ColumnValueMap> itemValueMap;
- QHash <QString, ColumnValueMap> itemRotationMap;
+ QHash<QString, ColumnValueMap> itemValueMap;
+ QHash<QString, ColumnValueMap> itemRotationMap;
+
+ bool cumulative = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBAverage
+ || m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBCumulative;
+ bool countMatches = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBAverage;
+ bool takeFirst = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBFirst;
+ QHash<QString, QHash<QString, int> > *matchCountMap = 0;
+ if (countMatches)
+ matchCountMap = new QHash<QString, QHash<QString, int> >;
+
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
QModelIndex index = m_itemModel->index(i, j);
@@ -193,7 +203,19 @@ void BarItemModelHandler::resolveModel()
value = valueVar.toString().replace(m_valuePattern, m_valueReplace).toFloat();
else
value = valueVar.toFloat();
- itemValueMap[rowRoleStr][columnRoleStr] = value;
+ if (countMatches)
+ (*matchCountMap)[rowRoleStr][columnRoleStr]++;
+
+ if (cumulative) {
+ itemValueMap[rowRoleStr][columnRoleStr] += value;
+ } else {
+ if (takeFirst && itemValueMap.contains(rowRoleStr)) {
+ if (itemValueMap.value(rowRoleStr).contains(columnRoleStr))
+ continue; // We already have a value for this row/column combo
+ }
+ itemValueMap[rowRoleStr][columnRoleStr] = value;
+ }
+
if (m_rotationRole != noRoleIndex) {
QVariant rotationVar = index.data(m_rotationRole);
float rotation;
@@ -203,7 +225,13 @@ void BarItemModelHandler::resolveModel()
} else {
rotation = rotationVar.toFloat();
}
- itemRotationMap[rowRoleStr][columnRoleStr] = rotation;
+ if (cumulative) {
+ itemRotationMap[rowRoleStr][columnRoleStr] += rotation;
+ } else {
+ // We know we are in take last mode if we get here,
+ // as take first mode skips to next loop already earlier
+ itemRotationMap[rowRoleStr][columnRoleStr] = rotation;
+ }
}
if (generateRows && !rowListHash.value(rowRoleStr, false)) {
rowListHash.insert(rowRoleStr, true);
@@ -239,9 +267,16 @@ void BarItemModelHandler::resolveModel()
QString rowKey = rowList.at(i);
QBarDataRow &newProxyRow = *m_proxyArray->at(i);
for (int j = 0; j < columnList.size(); j++) {
- newProxyRow[j].setValue(itemValueMap[rowKey][columnList.at(j)]);
- if (m_rotationRole != noRoleIndex)
- newProxyRow[j].setRotation(itemRotationMap[rowKey][columnList.at(j)]);
+ float value = itemValueMap[rowKey][columnList.at(j)];
+ if (countMatches)
+ value /= float((*matchCountMap)[rowKey][columnList.at(j)]);
+ newProxyRow[j].setValue(value);
+ if (m_rotationRole != noRoleIndex) {
+ float angle = itemRotationMap[rowKey][columnList.at(j)];
+ if (countMatches)
+ angle /= float((*matchCountMap)[rowKey][columnList.at(j)]);
+ newProxyRow[j].setRotation(angle);
+ }
}
}
diff --git a/src/datavisualization/data/qitemmodelbardataproxy.cpp b/src/datavisualization/data/qitemmodelbardataproxy.cpp
index cccf7d44..ba98ced1 100644
--- a/src/datavisualization/data/qitemmodelbardataproxy.cpp
+++ b/src/datavisualization/data/qitemmodelbardataproxy.cpp
@@ -237,6 +237,37 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty ItemModelBarDataProxy.MultiMatchBehavior ItemModelBarDataProxy::multiMatchBehavior
+ * This property defines how multiple matches for each row/column combination are handled.
+ * Defaults to ItemModelBarDataProxy.MMBLast. The chosen behavior affects both bar value
+ * and rotation.
+ *
+ * For example, you might have an item model with timestamped data taken at irregular intervals
+ * and you want to visualize total value of data items on each day with a bar graph.
+ * This can be done by specifying row and column categories so that each bar represents a day,
+ * and setting multiMatchBehavior to ItemModelBarDataProxy.MMBCumulative.
+ */
+
+/*!
+ * \enum QItemModelBarDataProxy::MultiMatchBehavior
+ *
+ * Behavior types for QItemModelBarDataProxy::multiMatchBehavior property.
+ *
+ * \value MMBFirst
+ * The value is taken from the first item in the item model that matches
+ * each row/column combination.
+ * \value MMBLast
+ * The value is taken from the last item in the item model that matches
+ * each row/column combination.
+ * \value MMBAverage
+ * The values from all items matching each row/column combination are
+ * averaged together and the average is used as the bar value.
+ * \value MMBCumulative
+ * The values from all items matching each row/column combination are
+ * added together and the total is used as the bar value.
+ */
+
+/*!
* Constructs QItemModelBarDataProxy with optional \a parent.
*/
QItemModelBarDataProxy::QItemModelBarDataProxy(QObject *parent)
@@ -784,6 +815,31 @@ QString QItemModelBarDataProxy::rotationRoleReplace() const
}
/*!
+ * \property QItemModelBarDataProxy::multiMatchBehavior
+ *
+ * This property defines how multiple matches for each row/column combination are handled.
+ * Defaults to QItemModelBarDataProxy::MMBLast. The chosen behavior affects both bar value
+ * and rotation.
+ *
+ * For example, you might have an item model with timestamped data taken at irregular intervals
+ * and you want to visualize total value of data items on each day with a bar graph.
+ * This can be done by specifying row and column categories so that each bar represents a day,
+ * and setting multiMatchBehavior to QItemModelBarDataProxy::MMBCumulative.
+ */
+void QItemModelBarDataProxy::setMultiMatchBehavior(QItemModelBarDataProxy::MultiMatchBehavior behavior)
+{
+ if (dptr()->m_multiMatchBehavior != behavior) {
+ dptr()->m_multiMatchBehavior = behavior;
+ emit multiMatchBehaviorChanged(behavior);
+ }
+}
+
+QItemModelBarDataProxy::MultiMatchBehavior QItemModelBarDataProxy::multiMatchBehavior() const
+{
+ return dptrc()->m_multiMatchBehavior;
+}
+
+/*!
* \internal
*/
QItemModelBarDataProxyPrivate *QItemModelBarDataProxy::dptr()
@@ -806,7 +862,8 @@ QItemModelBarDataProxyPrivate::QItemModelBarDataProxyPrivate(QItemModelBarDataPr
m_itemModelHandler(new BarItemModelHandler(q)),
m_useModelCategories(false),
m_autoRowCategories(true),
- m_autoColumnCategories(true)
+ m_autoColumnCategories(true),
+ m_multiMatchBehavior(QItemModelBarDataProxy::MMBLast)
{
}
@@ -858,6 +915,8 @@ void QItemModelBarDataProxyPrivate::connectItemModelHandler()
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
QObject::connect(qptr(), &QItemModelBarDataProxy::rotationRoleReplaceChanged,
m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
+ QObject::connect(qptr(), &QItemModelBarDataProxy::multiMatchBehaviorChanged,
+ m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qitemmodelbardataproxy.h b/src/datavisualization/data/qitemmodelbardataproxy.h
index ad7be3a7..317befdc 100644
--- a/src/datavisualization/data/qitemmodelbardataproxy.h
+++ b/src/datavisualization/data/qitemmodelbardataproxy.h
@@ -30,6 +30,7 @@ class QItemModelBarDataProxyPrivate;
class QT_DATAVISUALIZATION_EXPORT QItemModelBarDataProxy : public QBarDataProxy
{
Q_OBJECT
+ Q_ENUMS(MultiMatchBehavior)
Q_PROPERTY(const QAbstractItemModel* itemModel READ itemModel WRITE setItemModel NOTIFY itemModelChanged)
Q_PROPERTY(QString rowRole READ rowRole WRITE setRowRole NOTIFY rowRoleChanged)
Q_PROPERTY(QString columnRole READ columnRole WRITE setColumnRole NOTIFY columnRoleChanged)
@@ -48,8 +49,16 @@ class QT_DATAVISUALIZATION_EXPORT QItemModelBarDataProxy : public QBarDataProxy
Q_PROPERTY(QString columnRoleReplace READ columnRoleReplace WRITE setColumnRoleReplace NOTIFY columnRoleReplaceChanged REVISION 1)
Q_PROPERTY(QString valueRoleReplace READ valueRoleReplace WRITE setValueRoleReplace NOTIFY valueRoleReplaceChanged REVISION 1)
Q_PROPERTY(QString rotationRoleReplace READ rotationRoleReplace WRITE setRotationRoleReplace NOTIFY rotationRoleReplaceChanged REVISION 1)
+ Q_PROPERTY(MultiMatchBehavior multiMatchBehavior READ multiMatchBehavior WRITE setMultiMatchBehavior NOTIFY multiMatchBehaviorChanged REVISION 1)
public:
+ enum MultiMatchBehavior {
+ MMBFirst = 0,
+ MMBLast = 1,
+ MMBAverage = 2,
+ MMBCumulative = 3
+ };
+
explicit QItemModelBarDataProxy(QObject *parent = 0);
QItemModelBarDataProxy(const QAbstractItemModel *itemModel, QObject *parent = 0);
QItemModelBarDataProxy(const QAbstractItemModel *itemModel, const QString &valueRole,
@@ -120,6 +129,9 @@ public:
void setRotationRoleReplace(const QString &replace);
QString rotationRoleReplace() const;
+ void setMultiMatchBehavior(MultiMatchBehavior behavior);
+ MultiMatchBehavior multiMatchBehavior() const;
+
signals:
void itemModelChanged(const QAbstractItemModel* itemModel);
void rowRoleChanged(const QString &role);
@@ -139,6 +151,7 @@ signals:
Q_REVISION(1) void columnRoleReplaceChanged(const QString &replace);
Q_REVISION(1) void valueRoleReplaceChanged(const QString &replace);
Q_REVISION(1) void rotationRoleReplaceChanged(const QString &replace);
+ Q_REVISION(1) void multiMatchBehaviorChanged(MultiMatchBehavior behavior);
protected:
QItemModelBarDataProxyPrivate *dptr();
diff --git a/src/datavisualization/data/qitemmodelbardataproxy_p.h b/src/datavisualization/data/qitemmodelbardataproxy_p.h
index 3e8fa3ae..84564d02 100644
--- a/src/datavisualization/data/qitemmodelbardataproxy_p.h
+++ b/src/datavisualization/data/qitemmodelbardataproxy_p.h
@@ -73,6 +73,8 @@ private:
QString m_valueRoleReplace;
QString m_rotationRoleReplace;
+ QItemModelBarDataProxy::MultiMatchBehavior m_multiMatchBehavior;
+
friend class BarItemModelHandler;
friend class QItemModelBarDataProxy;
};
diff --git a/tests/qmlmultitest/qml/qmlmultitest/Data.qml b/tests/qmlmultitest/qml/qmlmultitest/Data.qml
index ddc0aad8..2ef168da 100644
--- a/tests/qmlmultitest/qml/qmlmultitest/Data.qml
+++ b/tests/qmlmultitest/qml/qmlmultitest/Data.qml
@@ -43,6 +43,27 @@ Item {
ListElement{ coords: "1,4"; data: "21.3/14.6/5.83"; }
ListElement{ coords: "2,4"; data: "22.5/14.8/7.32"; }
ListElement{ coords: "3,4"; data: "23.7/14.3/6.90"; }
+
+ ListElement{ coords: "0,0"; data: "40.0/30.0/14.75"; }
+ ListElement{ coords: "1,0"; data: "41.1/30.3/13.00"; }
+ ListElement{ coords: "2,0"; data: "42.5/30.7/11.24"; }
+ ListElement{ coords: "3,0"; data: "44.0/30.5/12.53"; }
+ ListElement{ coords: "0,1"; data: "40.2/31.2/13.55"; }
+ ListElement{ coords: "1,1"; data: "41.3/31.5/13.03"; }
+ ListElement{ coords: "2,1"; data: "42.6/31.7/13.46"; }
+ ListElement{ coords: "3,1"; data: "43.4/31.5/14.12"; }
+ ListElement{ coords: "0,2"; data: "40.2/32.3/13.37"; }
+ ListElement{ coords: "1,2"; data: "41.1/32.4/12.98"; }
+ ListElement{ coords: "2,2"; data: "42.5/32.1/13.33"; }
+ ListElement{ coords: "3,2"; data: "43.3/32.7/13.23"; }
+ ListElement{ coords: "0,3"; data: "40.7/33.3/15.34"; }
+ ListElement{ coords: "1,3"; data: "41.5/33.2/14.54"; }
+ ListElement{ coords: "2,3"; data: "42.4/33.6/14.65"; }
+ ListElement{ coords: "3,3"; data: "43.2/33.4/16.67"; }
+ ListElement{ coords: "0,4"; data: "40.6/35.0/16.01"; }
+ ListElement{ coords: "1,4"; data: "41.3/34.6/15.83"; }
+ ListElement{ coords: "2,4"; data: "42.5/34.8/17.32"; }
+ ListElement{ coords: "3,4"; data: "43.7/34.3/16.90"; }
}
}
diff --git a/tests/qmlmultitest/qml/qmlmultitest/main.qml b/tests/qmlmultitest/qml/qmlmultitest/main.qml
index 84eb4294..b5a62902 100644
--- a/tests/qmlmultitest/qml/qmlmultitest/main.qml
+++ b/tests/qmlmultitest/qml/qmlmultitest/main.qml
@@ -59,6 +59,7 @@ Rectangle {
Surface3DSeries {
itemLabelFormat: "Pop density at (@xLabel N, @zLabel E): @yLabel"
ItemModelSurfaceDataProxy {
+ id: surfaceProxy
itemModel: data.sharedData
// The surface data points are not neatly lined up in rows and columns,
// so we define explicit row and column roles.
@@ -118,10 +119,11 @@ Rectangle {
}
NewButton {
+ id: mmbButton
Layout.fillHeight: true
Layout.fillWidth: true
- text: "Toggle Mesh Styles"
- onClicked: toggleMeshStyle() // call a helper function to keep button itself simpler
+ text: "MMB: Last"
+ onClicked: changeMMB() // call a helper function to keep button itself simpler
}
}
}
@@ -146,6 +148,7 @@ Rectangle {
itemLabelFormat: "Pop density at (@xLabel N, @zLabel E): @yLabel"
mesh: Abstract3DSeries.MeshCube
ItemModelScatterDataProxy {
+ id: scatterProxy
itemModel: data.sharedData
// Mapping model roles to scatter series item coordinates.
xPosRole: "data"
@@ -187,17 +190,21 @@ Rectangle {
name: "Population density"
ItemModelBarDataProxy {
+ id: barProxy
itemModel: data.sharedData
// Mapping model roles to bar series rows, columns, and values.
rowRole: "coords"
columnRole: "coords"
valueRole: "data"
+ rotationRole: "coords"
rowRolePattern: /(\d),\d/
columnRolePattern: /(\d),(\d)/
valueRolePattern: /^([^\/]*)\/([^\/]*)\/(.*)$/
+ rotationRolePattern: /(\d)\,(\d)/
rowRoleReplace: "\\1"
columnRoleReplace: "\\2"
valueRoleReplace: "\\3"
+ rotationRoleReplace: "\\2\\1"
}
}
}
@@ -219,16 +226,19 @@ Rectangle {
barGraph.scene.activeCamera.zoomLevel = 100.0
}
- function toggleMeshStyle() {
- if (barGraph.seriesList[0].meshSmooth === true) {
- barGraph.seriesList[0].meshSmooth = false
- if (surfaceGraph.seriesList[0].flatShadingSupported)
- surfaceGraph.seriesList[0].flatShadingEnabled = true
- scatterGraph.seriesList[0].meshSmooth = false
+ function changeMMB() {
+ if (barProxy.multiMatchBehavior === ItemModelBarDataProxy.MMBLast) {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBAverage
+ mmbButton.text = "MMB: Average"
+ } else if (barProxy.multiMatchBehavior === ItemModelBarDataProxy.MMBAverage) {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBCumulative
+ mmbButton.text = "MMB: Cumulative"
+ } else if (barProxy.multiMatchBehavior === ItemModelBarDataProxy.MMBCumulative) {
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBFirst
+ mmbButton.text = "MMB: First"
} else {
- barGraph.seriesList[0].meshSmooth = true
- surfaceGraph.seriesList[0].flatShadingEnabled = false
- scatterGraph.seriesList[0].meshSmooth = true
+ barProxy.multiMatchBehavior = ItemModelBarDataProxy.MMBLast
+ mmbButton.text = "MMB: Last"
}
}
}