1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example qml3doscilloscope
\title Qt Quick 2 Oscilloscope Example
\ingroup qtdatavisualization_examples
\brief Example of a hybrid C++ and QML application.
The Qt Quick 2 oscilloscope example shows how to combine C++ and QML in an application,
as well as showing data that changes realtime.
\image qml3doscilloscope-example.png
The interesting thing about this example is combining C++ and QML, so we'll concentrate on
that and skip explaining the basic functionality - for
more detailed QML example documentation, see \l{Qt Quick 2 Scatter Example}.
\include examples-run.qdocinc
\section1 Data Source in C++
The item model based proxies are good for simple and/or static graphs, but to achieve
best performance when displaying data changing in realtime, the basic proxies should be used.
Those are not supported in QML, as the data items they store are not \l{QObject}s and
cannot therefore be directly manipulated from QML code.
To overcome this limitation, we implement a simple \c DataSource class in C++ to populate the
data proxy of the series.
The \c DataSource class provides two methods that can be called from QML:
\snippet qml3doscilloscope/datasource.h 0
The first method, \c generateData(), creates a cache of simulated oscilloscope data for us
to display. The data is cached in a format accepted by QSurfaceDataProxy:
\snippet qml3doscilloscope/datasource.cpp 0
The second method, \c update(), copies one set of the cached data into another array, which we
set to the data proxy of the series by calling QSurfaceDataProxy::resetArray().
We reuse the same array if the array dimensions have not changed to minimize overhead:
\snippet qml3doscilloscope/datasource.cpp 1
\note Even though we are operating on the array pointer we have previously set to the proxy
we still need to call QSurfaceDataProxy::resetArray() after changing the data in it to prompt
the graph to render the data.
To be able to access the \c DataSource methods from QML, we need to expose it. We do this by
making the DataSource a QML_ELEMENT:
\snippet qml3doscilloscope/datasource.h 1
In addition to that, it needs to be declared as a QML module in the CMakeLists.txt:
\badcode
qt6_add_qml_module(qml3doscilloscope
URI DataSource
VERSION 1.0
NO_RESOURCE_TARGET_PATH
SOURCES
datasource.cpp datasource.h
QML_FILES
qml/qml3doscilloscope/main.qml
)
\endcode
To make it possible to use QSurface3DSeries pointers as parameters on the
\c DataSource class methods on all environments and builds, we need to make sure the meta
type is registered:
\snippet qml3doscilloscope/datasource.cpp 3
\dots 0
\snippet qml3doscilloscope/datasource.cpp 4
\section1 QML
To be able to use the \c{DataSource}, we need to import the QML module, and create an
instance of it to be used:
\snippet qml3doscilloscope/qml/qml3doscilloscope/main.qml 0
\dots 0
\snippet qml3doscilloscope/qml/qml3doscilloscope/main.qml 5
We define a Surface3D graph normally and give it a Surface3DSeries:
\snippet qml3doscilloscope/qml/qml3doscilloscope/main.qml 0
One interesting detail is that we don't specify a proxy for the Surface3DSeries we attach
to the graph. This makes the series to utilize the default QSurfaceDataProxy.
We also hide the item label with \l{Abstract3DSeries::itemLabelVisible}{itemLabelVisible}, since
we want to display the selected item information in a \c Text element instead of a floating
label above the selection pointer.
This is done because the selection pointer moves around a lot as the data changes, which makes
the regular selection label difficult to read.
We initialize the \c DataSource cache when the graph is complete by calling a helper function
\c generateData(), which calls the method with the same name on the \c DataSource:
\snippet qml3doscilloscope/qml/qml3doscilloscope/main.qml 2
\dots 4
\snippet qml3doscilloscope/qml/qml3doscilloscope/main.qml 4
To trigger the updates in data, we define a \c Timer item which calls the \c update() method on the \c
DataSource at requested intervals. The label update is also triggered on each cycle:
\snippet qml3doscilloscope/qml/qml3doscilloscope/main.qml 3
\section1 Enabling Direct Rendering
Since this application potentially deals with a lot of rapidly changing data, we use direct
rendering mode for performance. To enable antialiasing in this mode the surface format of the application
window needs to be changed, as the default format used by QQuickView doesn't support antialiasing.
We use the utility function provided by Qt Data Visualization to change the surface format
in \c main.cpp:
\snippet qml3doscilloscope/main.cpp 1
\dots 0
\snippet qml3doscilloscope/main.cpp 2
*/
|