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

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