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