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 <QtDataVisualization/q3dbars.h> |
31 | #include <QtDataVisualization/q3dsurface.h> |
32 | #include <QtDataVisualization/qcategory3daxis.h> |
33 | #include <QtDataVisualization/qitemmodelbardataproxy.h> |
34 | #include <QtDataVisualization/qitemmodelsurfacedataproxy.h> |
35 | #include <QtDataVisualization/qvalue3daxis.h> |
36 | #include <QtDataVisualization/q3dscene.h> |
37 | #include <QtDataVisualization/q3dcamera.h> |
38 | #include <QtDataVisualization/qbar3dseries.h> |
39 | #include <QtDataVisualization/q3dtheme.h> |
40 | |
41 | #include <QtWidgets/QApplication> |
42 | #include <QtWidgets/QVBoxLayout> |
43 | #include <QtWidgets/QTableWidget> |
44 | #include <QtGui/QScreen> |
45 | #include <QtCore/QTimer> |
46 | #include <QtCore/QRandomGenerator> |
47 | #include <QtCore/QDebug> |
48 | #include <QtWidgets/QHeaderView> |
49 | #include <QtWidgets/QPushButton> |
50 | |
51 | #define USE_STATIC_DATA |
52 | |
53 | using namespace QtDataVisualization; |
54 | |
55 | class GraphDataGenerator : public QObject |
56 | { |
57 | public: |
58 | explicit GraphDataGenerator(Q3DBars *bargraph, Q3DSurface * surfaceGraph, |
59 | QTableWidget *tableWidget); |
60 | ~GraphDataGenerator(); |
61 | |
62 | void setupModel(); |
63 | void addRow(); |
64 | void start(); |
65 | void selectFromTable(const QPoint &selection); |
66 | void selectedFromTable(int currentRow, int currentColumn, int previousRow, int previousColumn); |
67 | void fixTableSize(); |
68 | void changeSelectedButtonClicked(); |
69 | |
70 | private: |
71 | Q3DBars *m_barGraph; |
72 | Q3DSurface *m_surfaceGraph; |
73 | QTimer *m_dataTimer; |
74 | QTimer *m_styleTimer; |
75 | QTimer *m_presetTimer; |
76 | QTimer *m_themeTimer; |
77 | int m_columnCount; |
78 | int m_rowCount; |
79 | QTableWidget *m_tableWidget; // not owned |
80 | }; |
81 | |
82 | GraphDataGenerator::GraphDataGenerator(Q3DBars *bargraph, Q3DSurface * surfaceGraph, |
83 | QTableWidget *tableWidget) |
84 | : m_barGraph(bargraph), |
85 | m_surfaceGraph(surfaceGraph), |
86 | m_dataTimer(0), |
87 | m_styleTimer(0), |
88 | m_presetTimer(0), |
89 | m_themeTimer(0), |
90 | m_columnCount(100), |
91 | m_rowCount(50), |
92 | m_tableWidget(tableWidget) |
93 | { |
94 | // Set up bar specifications; make the bars as wide as they are deep, |
95 | // and add a small space between them |
96 | m_barGraph->setBarThickness(1.0f); |
97 | m_barGraph->setBarSpacing(QSizeF(0.2, 0.2)); |
98 | |
99 | #ifndef USE_STATIC_DATA |
100 | // Set up sample space; make it as deep as it's wide |
101 | m_barGraph->rowAxis()->setRange(0, m_rowCount); |
102 | m_barGraph->columnAxis()->setRange(0, m_columnCount); |
103 | m_tableWidget->setColumnCount(m_columnCount); |
104 | |
105 | // Set selection mode to full |
106 | m_barGraph->setSelectionMode(QAbstract3DGraph::SelectionItemRowAndColumn); |
107 | |
108 | // Hide axis labels by explicitly setting one empty string as label list |
109 | m_barGraph->rowAxis()->setLabels(QStringList(QString())); |
110 | m_barGraph->columnAxis()->setLabels(QStringList(QString())); |
111 | |
112 | m_barGraph->seriesList().at(0)->setItemLabelFormat(QStringLiteral("@valueLabel" )); |
113 | #else |
114 | // Set selection mode to slice row |
115 | m_barGraph->setSelectionMode( |
116 | QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice); |
117 | m_surfaceGraph->setSelectionMode( |
118 | QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice); |
119 | #endif |
120 | } |
121 | |
122 | GraphDataGenerator::~GraphDataGenerator() |
123 | { |
124 | if (m_dataTimer) { |
125 | m_dataTimer->stop(); |
126 | delete m_dataTimer; |
127 | } |
128 | delete m_barGraph; |
129 | delete m_surfaceGraph; |
130 | } |
131 | |
132 | void GraphDataGenerator::start() |
133 | { |
134 | #ifndef USE_STATIC_DATA |
135 | m_dataTimer = new QTimer(); |
136 | m_dataTimer->setTimerType(Qt::CoarseTimer); |
137 | QObject::connect(m_dataTimer, &QTimer::timeout, this, &GraphDataGenerator::addRow); |
138 | m_dataTimer->start(0); |
139 | m_tableWidget->setFixedWidth(m_graph->width()); |
140 | #else |
141 | setupModel(); |
142 | |
143 | // Table needs to be shown before the size of its headers can be accurately obtained, |
144 | // so we postpone it a bit |
145 | m_dataTimer = new QTimer(); |
146 | m_dataTimer->setSingleShot(true); |
147 | QObject::connect(sender: m_dataTimer, signal: &QTimer::timeout, receiver: this, slot: &GraphDataGenerator::fixTableSize); |
148 | m_dataTimer->start(msec: 0); |
149 | #endif |
150 | } |
151 | |
152 | void GraphDataGenerator::setupModel() |
153 | { |
154 | // Set up row and column names |
155 | QStringList days; |
156 | days << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday" << "Saturday" << "Sunday" ; |
157 | QStringList weeks; |
158 | weeks << "week 1" << "week 2" << "week 3" << "week 4" << "week 5" ; |
159 | |
160 | // Set up data |
161 | const char *hours[5][7] = |
162 | // Mon Tue Wed Thu Fri Sat Sun |
163 | {{"9/10/2.0/30" , "9/11/1.0/30" , "9/12/3.0/30" , "9/13/0.2/30" , "9/14/1.0/30" , "9/15/5.0/30" , "9/16/10.0/30" }, // week 1 |
164 | {"8/10/0.5/45" , "8/11/1.0/45" , "8/12/3.0/45" , "8/13/1.0/45" , "8/14/2.0/45" , "8/15/2.0/45" , "8/16/3.0/45" }, // week 2 |
165 | {"7/10/1.0/60" , "7/11/1.0/60" , "7/12/2.0/60" , "7/13/1.0/60" , "7/14/4.0/60" , "7/15/4.0/60" , "7/16/4.0/60" }, // week 3 |
166 | {"6/10/0.0/75" , "6/11/1.0/75" , "6/12/0.0/75" , "6/13/0.0/75" , "6/14/2.0/75" , "6/15/2.0/75" , "6/16/0.3/75" }, // week 4 |
167 | {"5/10/3.0/90" , "5/11/3.0/90" , "5/12/6.0/90" , "5/13/2.0/90" , "5/14/2.0/90" , "5/15/1.0/90" , "5/16/1.0/90" }}; // week 5 |
168 | |
169 | // Add labels |
170 | m_barGraph->rowAxis()->setTitle("Week of year" ); |
171 | m_barGraph->columnAxis()->setTitle("Day of week" ); |
172 | m_barGraph->valueAxis()->setTitle("Hours spent on the Internet" ); |
173 | m_barGraph->valueAxis()->setLabelFormat("%.1f h" ); |
174 | |
175 | m_surfaceGraph->axisZ()->setTitle("Week of year" ); |
176 | m_surfaceGraph->axisX()->setTitle("Day of week" ); |
177 | m_surfaceGraph->axisY()->setTitle("Hours spent on the Internet" ); |
178 | m_surfaceGraph->axisY()->setLabelFormat("%.1f h" ); |
179 | |
180 | m_tableWidget->setRowCount(5); |
181 | m_tableWidget->setColumnCount(7); |
182 | m_tableWidget->setHorizontalHeaderLabels(days); |
183 | m_tableWidget->setVerticalHeaderLabels(weeks); |
184 | m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
185 | m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
186 | m_tableWidget->setCurrentCell(row: -1, column: -1); |
187 | |
188 | for (int week = 0; week < weeks.size(); week++) { |
189 | for (int day = 0; day < days.size(); day++) { |
190 | QModelIndex index = m_tableWidget->model()->index(row: week, column: day); |
191 | m_tableWidget->model()->setData(index, value: hours[week][day]); |
192 | } |
193 | } |
194 | } |
195 | |
196 | void GraphDataGenerator::addRow() |
197 | { |
198 | m_tableWidget->model()->insertRow(arow: 0); |
199 | if (m_tableWidget->model()->rowCount() > m_rowCount) |
200 | m_tableWidget->model()->removeRow(arow: m_rowCount); |
201 | for (int i = 0; i < m_columnCount; i++) { |
202 | QModelIndex index = m_tableWidget->model()->index(row: 0, column: i); |
203 | m_tableWidget->model()->setData(index, |
204 | value: ((float)i / (float)m_columnCount) / 2.0f |
205 | + QRandomGenerator::global()->bounded(highest: 30.0 / 100.0f)); |
206 | } |
207 | m_tableWidget->resizeColumnsToContents(); |
208 | } |
209 | |
210 | void GraphDataGenerator::selectFromTable(const QPoint &selection) |
211 | { |
212 | m_tableWidget->setFocus(); |
213 | m_tableWidget->setCurrentCell(row: selection.x(), column: selection.y()); |
214 | } |
215 | |
216 | void GraphDataGenerator::selectedFromTable(int currentRow, int currentColumn, |
217 | int previousRow, int previousColumn) |
218 | { |
219 | Q_UNUSED(previousRow) |
220 | Q_UNUSED(previousColumn) |
221 | m_barGraph->seriesList().at(i: 0)->setSelectedBar(QPoint(currentRow, currentColumn)); |
222 | m_surfaceGraph->seriesList().at(i: 0)->setSelectedPoint(QPoint(currentRow, currentColumn)); |
223 | } |
224 | |
225 | void GraphDataGenerator::fixTableSize() |
226 | { |
227 | int width = m_tableWidget->horizontalHeader()->length(); |
228 | width += m_tableWidget->verticalHeader()->width(); |
229 | m_tableWidget->setFixedWidth(width + 2); |
230 | int height = m_tableWidget->verticalHeader()->length(); |
231 | height += m_tableWidget->horizontalHeader()->height(); |
232 | m_tableWidget->setFixedHeight(height + 2); |
233 | } |
234 | |
235 | void GraphDataGenerator::changeSelectedButtonClicked() |
236 | { |
237 | // Change all selected cells to a random value 1-10 |
238 | QVariant value = QVariant::fromValue(value: QRandomGenerator::global()->bounded(highest: 10.0) + 1); |
239 | QList<QTableWidgetItem *> selectedItems = m_tableWidget->selectedItems(); |
240 | foreach (QTableWidgetItem *item, selectedItems) { |
241 | QString oldData = item->data(role: Qt::DisplayRole).toString(); |
242 | item->setData(role: Qt::DisplayRole, |
243 | value: oldData.left(n: 5) |
244 | .append(s: QString::number(value.toReal())) |
245 | .append(s: "/" ) |
246 | .append(s: QString::number(value.toReal() * 10))); |
247 | } |
248 | } |
249 | |
250 | int main(int argc, char **argv) |
251 | { |
252 | QApplication app(argc, argv); |
253 | Q3DBars *barGraph = new Q3DBars(); |
254 | Q3DSurface *surfaceGraph = new Q3DSurface(); |
255 | QWidget *barContainer = QWidget::createWindowContainer(window: barGraph); |
256 | QWidget *surfaceContainer = QWidget::createWindowContainer(window: surfaceGraph); |
257 | |
258 | barContainer->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding); |
259 | barContainer->setFocusPolicy(Qt::StrongFocus); |
260 | surfaceContainer->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding); |
261 | surfaceContainer->setFocusPolicy(Qt::StrongFocus); |
262 | |
263 | QWidget widget; |
264 | QVBoxLayout *mainLayout = new QVBoxLayout(&widget); |
265 | QHBoxLayout *graphLayout = new QHBoxLayout(); |
266 | QVBoxLayout *buttonLayout = new QVBoxLayout(); |
267 | QHBoxLayout *bottomLayout = new QHBoxLayout(); |
268 | QTableWidget *tableWidget = new QTableWidget(&widget); |
269 | QPushButton *changeSelectedButton = new QPushButton(&widget); |
270 | changeSelectedButton->setText(QStringLiteral("Change Selected" )); |
271 | |
272 | buttonLayout->addWidget(changeSelectedButton); |
273 | graphLayout->addWidget(barContainer); |
274 | graphLayout->addWidget(surfaceContainer); |
275 | bottomLayout->addLayout(layout: buttonLayout); |
276 | bottomLayout->addWidget(tableWidget); |
277 | mainLayout->addLayout(layout: graphLayout); |
278 | mainLayout->addLayout(layout: bottomLayout); |
279 | |
280 | tableWidget->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed); |
281 | tableWidget->setAlternatingRowColors(true); |
282 | widget.setWindowTitle(QStringLiteral("Hours spent on the Internet" )); |
283 | |
284 | // Since we are dealing with QTableWidget, the model will already have data sorted properly |
285 | // into rows and columns, so we simply set useModelCategories property to true to utilize this. |
286 | QItemModelBarDataProxy *barProxy = new QItemModelBarDataProxy(tableWidget->model()); |
287 | QItemModelSurfaceDataProxy *surfaceProxy = new QItemModelSurfaceDataProxy(tableWidget->model()); |
288 | barProxy->setUseModelCategories(true); |
289 | surfaceProxy->setUseModelCategories(true); |
290 | barProxy->setRotationRole(tableWidget->model()->roleNames().value(akey: Qt::DisplayRole)); |
291 | barProxy->setValueRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$" ))); |
292 | barProxy->setRotationRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)\\d*\\/\\d*([\\.\\,]?)\\d*(\\/)(\\d*[\\.\\,]?\\d*)$" ))); |
293 | barProxy->setValueRoleReplace(QStringLiteral("\\4" )); |
294 | barProxy->setRotationRoleReplace(QStringLiteral("\\5" )); |
295 | surfaceProxy->setXPosRole(tableWidget->model()->roleNames().value(akey: Qt::DisplayRole)); |
296 | surfaceProxy->setZPosRole(tableWidget->model()->roleNames().value(akey: Qt::DisplayRole)); |
297 | surfaceProxy->setXPosRolePattern(QRegExp(QStringLiteral("^(\\d*)\\/(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$" ))); |
298 | surfaceProxy->setXPosRoleReplace(QStringLiteral("\\2" )); |
299 | surfaceProxy->setYPosRolePattern(QRegExp(QStringLiteral("^\\d*(\\/)(\\d*)\\/(\\d*[\\.\\,]?\\d*)\\/\\d*[\\.\\,]?\\d*$" ))); |
300 | surfaceProxy->setYPosRoleReplace(QStringLiteral("\\3" )); |
301 | surfaceProxy->setZPosRolePattern(QRegExp(QStringLiteral("^(\\d*)(\\/)(\\d*)\\/\\d*[\\.\\,]?\\d*\\/\\d*[\\.\\,]?\\d*$" ))); |
302 | surfaceProxy->setZPosRoleReplace(QStringLiteral("\\1" )); |
303 | QBar3DSeries *barSeries = new QBar3DSeries(barProxy); |
304 | QSurface3DSeries *surfaceSeries = new QSurface3DSeries(surfaceProxy); |
305 | barSeries->setMesh(QAbstract3DSeries::MeshPyramid); |
306 | barGraph->addSeries(series: barSeries); |
307 | surfaceGraph->addSeries(series: surfaceSeries); |
308 | |
309 | barGraph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetBehind); |
310 | surfaceGraph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); |
311 | |
312 | GraphDataGenerator generator(barGraph, surfaceGraph, tableWidget); |
313 | QObject::connect(sender: barSeries, signal: &QBar3DSeries::selectedBarChanged, receiver: &generator, |
314 | slot: &GraphDataGenerator::selectFromTable); |
315 | QObject::connect(sender: surfaceSeries, signal: &QSurface3DSeries::selectedPointChanged, receiver: &generator, |
316 | slot: &GraphDataGenerator::selectFromTable); |
317 | QObject::connect(sender: tableWidget, signal: &QTableWidget::currentCellChanged, receiver: &generator, |
318 | slot: &GraphDataGenerator::selectedFromTable); |
319 | QObject::connect(sender: changeSelectedButton, signal: &QPushButton::clicked, receiver: &generator, |
320 | slot: &GraphDataGenerator::changeSelectedButtonClicked); |
321 | |
322 | QSize screenSize = barGraph->screen()->size(); |
323 | widget.resize(QSize(screenSize.width() / 2, screenSize.height() / 2)); |
324 | widget.show(); |
325 | generator.start(); |
326 | return app.exec(); |
327 | } |
328 | |