1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "baritemmodelhandler_p.h"
31
32QT_BEGIN_NAMESPACE_DATAVISUALIZATION
33
34static const int noRoleIndex = -1;
35
36BarItemModelHandler::BarItemModelHandler(QItemModelBarDataProxy *proxy, QObject *parent)
37 : AbstractItemModelHandler(parent),
38 m_proxy(proxy),
39 m_proxyArray(0),
40 m_columnCount(0),
41 m_valueRole(noRoleIndex),
42 m_rotationRole(noRoleIndex),
43 m_haveValuePattern(false),
44 m_haveRotationPattern(false)
45{
46}
47
48BarItemModelHandler::~BarItemModelHandler()
49{
50}
51
52void BarItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
53 const QModelIndex &bottomRight,
54 const QVector<int> &roles)
55{
56 // Do nothing if full reset already pending
57 if (!m_fullReset) {
58 if (!m_proxy->useModelCategories()) {
59 // If the data model doesn't directly map rows and columns, we cannot optimize
60 AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
61 } else {
62 int startRow = qMin(a: topLeft.row(), b: bottomRight.row());
63 int endRow = qMax(a: topLeft.row(), b: bottomRight.row());
64 int startCol = qMin(a: topLeft.column(), b: bottomRight.column());
65 int endCol = qMax(a: topLeft.column(), b: bottomRight.column());
66
67 for (int i = startRow; i <= endRow; i++) {
68 for (int j = startCol; j <= endCol; j++) {
69 QModelIndex index = m_itemModel->index(row: i, column: j);
70 QBarDataItem item;
71 QVariant valueVar = index.data(arole: m_valueRole);
72 float value;
73 if (m_haveValuePattern)
74 value = valueVar.toString().replace(rx: m_valuePattern, after: m_valueReplace).toFloat();
75 else
76 value = valueVar.toFloat();
77 item.setValue(value);
78 if (m_rotationRole != noRoleIndex) {
79 QVariant rotationVar = index.data(arole: m_rotationRole);
80 float rotation;
81 if (m_haveRotationPattern) {
82 rotation = rotationVar.toString().replace(rx: m_rotationPattern,
83 after: m_rotationReplace).toFloat();
84 } else {
85 rotation = rotationVar.toFloat();
86 }
87 item.setRotation(rotation);
88 }
89 m_proxy->setItem(rowIndex: i, columnIndex: j, item);
90 }
91 }
92 }
93 }
94}
95
96// Resolve entire item model into QBarDataArray.
97void BarItemModelHandler::resolveModel()
98{
99 if (m_itemModel.isNull()) {
100 m_proxy->resetArray(newArray: 0);
101 return;
102 }
103
104 if (!m_proxy->useModelCategories()
105 && (m_proxy->rowRole().isEmpty() || m_proxy->columnRole().isEmpty())) {
106 m_proxy->resetArray(newArray: 0);
107 return;
108 }
109
110 // Value and rotation patterns can be reused on single item changes,
111 // so store them to member variables.
112 QRegExp rowPattern(m_proxy->rowRolePattern());
113 QRegExp colPattern(m_proxy->columnRolePattern());
114 m_valuePattern = m_proxy->valueRolePattern();
115 m_rotationPattern = m_proxy->rotationRolePattern();
116 QString rowReplace = m_proxy->rowRoleReplace();
117 QString colReplace = m_proxy->columnRoleReplace();
118 m_valueReplace = m_proxy->valueRoleReplace();
119 m_rotationReplace = m_proxy->rotationRoleReplace();
120 bool haveRowPattern = !rowPattern.isEmpty() && rowPattern.isValid();
121 bool haveColPattern = !colPattern.isEmpty() && colPattern.isValid();
122 m_haveValuePattern = !m_valuePattern.isEmpty() && m_valuePattern.isValid();
123 m_haveRotationPattern = !m_rotationPattern.isEmpty() && m_rotationPattern.isValid();
124
125 QStringList rowLabels;
126 QStringList columnLabels;
127
128 QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
129
130 // Default value role to display role if no mapping
131 m_valueRole = roleHash.key(avalue: m_proxy->valueRole().toLatin1(), defaultValue: Qt::DisplayRole);
132 m_rotationRole = roleHash.key(avalue: m_proxy->rotationRole().toLatin1(), defaultValue: noRoleIndex);
133 int rowCount = m_itemModel->rowCount();
134 int columnCount = m_itemModel->columnCount();
135
136 if (m_proxy->useModelCategories()) {
137 // If dimensions have changed, recreate the array
138 if (m_proxyArray != m_proxy->array() || columnCount != m_columnCount
139 || rowCount != m_proxyArray->size()) {
140 m_proxyArray = new QBarDataArray;
141 m_proxyArray->reserve(alloc: rowCount);
142 for (int i = 0; i < rowCount; i++)
143 m_proxyArray->append(t: new QBarDataRow(columnCount));
144 }
145 for (int i = 0; i < rowCount; i++) {
146 QBarDataRow &newProxyRow = *m_proxyArray->at(i);
147 for (int j = 0; j < columnCount; j++) {
148 QModelIndex index = m_itemModel->index(row: i, column: j);
149 QVariant valueVar = index.data(arole: m_valueRole);
150 float value;
151 if (m_haveValuePattern)
152 value = valueVar.toString().replace(rx: m_valuePattern, after: m_valueReplace).toFloat();
153 else
154 value = valueVar.toFloat();
155 newProxyRow[j].setValue(value);
156 if (m_rotationRole != noRoleIndex) {
157 QVariant rotationVar = index.data(arole: m_rotationRole);
158 float rotation;
159 if (m_haveRotationPattern) {
160 rotation = rotationVar.toString().replace(rx: m_rotationPattern,
161 after: m_rotationReplace).toFloat();
162 } else {
163 rotation = rotationVar.toFloat();
164 }
165 newProxyRow[j].setRotation(rotation);
166 }
167 }
168 }
169 // Generate labels from headers if using model rows/columns
170 for (int i = 0; i < rowCount; i++)
171 rowLabels << m_itemModel->headerData(section: i, orientation: Qt::Vertical).toString();
172 for (int i = 0; i < columnCount; i++)
173 columnLabels << m_itemModel->headerData(section: i, orientation: Qt::Horizontal).toString();
174 m_columnCount = columnCount;
175 } else {
176 int rowRole = roleHash.key(avalue: m_proxy->rowRole().toLatin1());
177 int columnRole = roleHash.key(avalue: m_proxy->columnRole().toLatin1());
178
179 bool generateRows = m_proxy->autoRowCategories();
180 bool generateColumns = m_proxy->autoColumnCategories();
181 QStringList rowList;
182 QStringList columnList;
183 // For detecting duplicates in categories generation, using QHashes should be faster than
184 // simple QStringList::contains() check.
185 QHash<QString, bool> rowListHash;
186 QHash<QString, bool> columnListHash;
187
188 // Sort values into rows and columns
189 typedef QHash<QString, float> ColumnValueMap;
190 QHash<QString, ColumnValueMap> itemValueMap;
191 QHash<QString, ColumnValueMap> itemRotationMap;
192
193 bool cumulative = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBAverage
194 || m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBCumulative;
195 bool countMatches = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBAverage;
196 bool takeFirst = m_proxy->multiMatchBehavior() == QItemModelBarDataProxy::MMBFirst;
197 QHash<QString, QHash<QString, int> > *matchCountMap = 0;
198 if (countMatches)
199 matchCountMap = new QHash<QString, QHash<QString, int> >;
200
201 for (int i = 0; i < rowCount; i++) {
202 for (int j = 0; j < columnCount; j++) {
203 QModelIndex index = m_itemModel->index(row: i, column: j);
204 QString rowRoleStr = index.data(arole: rowRole).toString();
205 if (haveRowPattern)
206 rowRoleStr.replace(rx: rowPattern, after: rowReplace);
207 QString columnRoleStr = index.data(arole: columnRole).toString();
208 if (haveColPattern)
209 columnRoleStr.replace(rx: colPattern, after: colReplace);
210 QVariant valueVar = index.data(arole: m_valueRole);
211 float value;
212 if (m_haveValuePattern)
213 value = valueVar.toString().replace(rx: m_valuePattern, after: m_valueReplace).toFloat();
214 else
215 value = valueVar.toFloat();
216 if (countMatches)
217 (*matchCountMap)[rowRoleStr][columnRoleStr]++;
218
219 if (cumulative) {
220 itemValueMap[rowRoleStr][columnRoleStr] += value;
221 } else {
222 if (takeFirst && itemValueMap.contains(akey: rowRoleStr)) {
223 if (itemValueMap.value(akey: rowRoleStr).contains(akey: columnRoleStr))
224 continue; // We already have a value for this row/column combo
225 }
226 itemValueMap[rowRoleStr][columnRoleStr] = value;
227 }
228
229 if (m_rotationRole != noRoleIndex) {
230 QVariant rotationVar = index.data(arole: m_rotationRole);
231 float rotation;
232 if (m_haveRotationPattern) {
233 rotation = rotationVar.toString().replace(rx: m_rotationPattern,
234 after: m_rotationReplace).toFloat();
235 } else {
236 rotation = rotationVar.toFloat();
237 }
238 if (cumulative) {
239 itemRotationMap[rowRoleStr][columnRoleStr] += rotation;
240 } else {
241 // We know we are in take last mode if we get here,
242 // as take first mode skips to next loop already earlier
243 itemRotationMap[rowRoleStr][columnRoleStr] = rotation;
244 }
245 }
246 if (generateRows && !rowListHash.value(akey: rowRoleStr, adefaultValue: false)) {
247 rowListHash.insert(akey: rowRoleStr, avalue: true);
248 rowList << rowRoleStr;
249 }
250 if (generateColumns && !columnListHash.value(akey: columnRoleStr, adefaultValue: false)) {
251 columnListHash.insert(akey: columnRoleStr, avalue: true);
252 columnList << columnRoleStr;
253 }
254 }
255 }
256
257 if (generateRows)
258 m_proxy->dptr()->m_rowCategories = rowList;
259 else
260 rowList = m_proxy->rowCategories();
261
262 if (generateColumns)
263 m_proxy->dptr()->m_columnCategories = columnList;
264 else
265 columnList = m_proxy->columnCategories();
266
267 // If dimensions have changed, recreate the array
268 if (m_proxyArray != m_proxy->array() || columnList.size() != m_columnCount
269 || rowList.size() != m_proxyArray->size()) {
270 m_proxyArray = new QBarDataArray;
271 m_proxyArray->reserve(alloc: rowList.size());
272 for (int i = 0; i < rowList.size(); i++)
273 m_proxyArray->append(t: new QBarDataRow(columnList.size()));
274 }
275 // Create new data array from itemValueMap
276 for (int i = 0; i < rowList.size(); i++) {
277 QString rowKey = rowList.at(i);
278 QBarDataRow &newProxyRow = *m_proxyArray->at(i);
279 for (int j = 0; j < columnList.size(); j++) {
280 float value = itemValueMap[rowKey][columnList.at(i: j)];
281 if (countMatches)
282 value /= float((*matchCountMap)[rowKey][columnList.at(i: j)]);
283 newProxyRow[j].setValue(value);
284 if (m_rotationRole != noRoleIndex) {
285 float angle = itemRotationMap[rowKey][columnList.at(i: j)];
286 if (countMatches)
287 angle /= float((*matchCountMap)[rowKey][columnList.at(i: j)]);
288 newProxyRow[j].setRotation(angle);
289 }
290 }
291 }
292
293 rowLabels = rowList;
294 columnLabels = columnList;
295 m_columnCount = columnList.size();
296
297 delete matchCountMap;
298 }
299
300 m_proxy->resetArray(newArray: m_proxyArray, rowLabels, columnLabels);
301}
302
303QT_END_NAMESPACE_DATAVISUALIZATION
304

source code of qtdatavis3d/src/datavisualization/data/baritemmodelhandler.cpp