Simple Scatter Graph

使用 Scatter3D in a QML application.

Simple Scatter Graph shows how to make a simple scatter graph visualization using Scatter3D and QML.

For instructions about how to interact with the graph, see this page .

For instructions on how to create a new Qt Quick application of your own, see Qt Creator help.

运行范例

要运行范例从 Qt Creator ,打开 欢迎 模式,然后选择范例从 范例 。更多信息,拜访 构建和运行范例 .

Setting up the Layout

Before diving into the QML code, main.cpp sets up the application. The main.qml file is read from resource ( qrc: )

viewer.setSource(QUrl("qrc:/qml/scatter/main.qml"));
					

This main.qml file is the starting point for the application's QML code. Firstly, import all the required QML modules:

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtGraphs
					

Then, create a 数据 item which will be the item that holds the data for the graph.

Data {
    id: seriesData
}
					

Next, create two Theme3D items that define two different themes for the graph, a Qt theme and a retro theme.

Theme3D {
    id: themeQt
    type: Theme3D.Theme.Qt
    font.pointSize: 40
}
Theme3D {
    id: themeRetro
    type: Theme3D.Theme.Retro
}
					

The layout for this application is going to consist of five different buttons to toggle different visual options for the graph on and off, and of course the graph itself. There are many ways to organize these, one way is to have a GridLayout containing four buttons, the Graph , and the fifth button, all appearing in a ColumnLayout .

ColumnLayout {
    id: mainLayout
    anchors.fill: parent
    anchors.margins: margin
    spacing: spacing
					

The GridLayout is responsive. What this means is that the number of columns used depend on the width of the application window. This is done by assigning the columns property a ternary operator, resolving to either 1, 2 or 4, depending on the window width.

GridLayout {
    Layout.fillWidth: true
    rowSpacing: spacing
    columnSpacing: spacing
    columns: mainView.width < mainView.buttonMinWidth * 2 + mainView.spacing + mainView.margin * 2 // width of 2 buttons
             ? 1
             : (mainView.width < mainView.buttonMinWidth * 4 + mainView.spacing * 3 + mainView.margin * 2 // width of 4 buttons
                ? 2
                : 4)
					

In this GridLayout , four RoundButton s are instantiated. One for toggling shadows, one for mesh smoothing, one for camera position, and finally, one for changing the background of the graph. All the buttons follow the same structure. Rather than redefining this structure for all buttons, a custom component can be made.

component CustomButton : RoundButton {
    id: buttonRoot
					

CustomButtom extends the RoundButton type, but adds a few additions. Properties such as Layout.minimumWidth , Layout.FillWidth , radius and background are set. These define the styling and layout of the button.

property alias source: iconImage.source
Layout.minimumWidth: buttonMinWidth
Layout.fillWidth: true
radius: mainView.radius
background: Rectangle {
    radius: mainView.radius
    color: "white"
    border.color: "black"
}
					

Whereas the contentItem defines the content of the button, in this case a Row type containing an icon and some text. The icon is imported using an IconImage type, and setting the source property. The text is created using a Label type and set using its text property. Two alliases are created for these two properties at the top of the custom component, this will allow them to be set when an instance of CustomButton 被创建。

contentItem: Row {
    id :content
    IconImage {
        id: iconImage
        width: iconDimension
        height: iconDimension
        color: "transparent"
    }
    Label {
        text: buttonRoot.text
        horizontalAlignment: Text.AlignLeft
        anchors.verticalCenter: parent.verticalCenter
    }
}
					

GridLayout , four CustomButton s are created. Three properties need to be set, the two alliases for the icon image and the text, and the signal onClicked .

CustomButton {
    id: shadowButton
    text: graph.shadowQuality === AbstractGraph3D.ShadowQuality.None ?
              qsTr("Show Shadows") : qsTr("Hide Shadows")
    source: graph.shadowQuality === AbstractGraph3D.ShadowQuality.None ?
                "qrc:/images/shadow.svg" : "qrc:/images/shadow_hide.svg"
    onClicked: {
        graph.shadowQuality = graph.shadowQuality === AbstractGraph3D.ShadowQuality.None ?
                    AbstractGraph3D.ShadowQuality.High :
                    AbstractGraph3D.ShadowQuality.None
    }
}
					

For example, the shadow button sets the onClicked sign to toggle the shadow on and off.

设置图形

Next in the layout is the Graph , this is defined in its own QML file Graph.qml , and is instantiated as follows:

Graph {
    id: graph
    Layout.fillWidth: true
    Layout.fillHeight: true
}
					

Looking at its implementation in Graph.qml , the graph is of type Scatter3D , nested within an Item 类型。

Scatter3D {
    id: scatterGraph
					

Before defining the data sets, some of the axes properties are configured.

axisX.segmentCount: 3
axisX.subSegmentCount: 2
axisX.labelFormat: "%.2f"
axisZ.segmentCount: 2
axisZ.subSegmentCount: 2
axisZ.labelFormat: "%.2f"
axisY.segmentCount: 2
axisY.subSegmentCount: 2
axisY.labelFormat: "%.2f"
					

Setting up the Data

The graph shows three datasets, representing the data given in Data.qml . The datasets are stored in ListModel 类型

ListModel {
    id: dataModel
    ListElement{ xPos: -10.0; yPos: 5.0; zPos: -5.0 }
    ...
ListElement{ xPos: -7.54 ; yPos: 2.8 ; zPos: -3.68 }
}
ListModel {
id: dataModelTwo
ListElement{ xPos: 2.25 ; yPos: 1.36 ; zPos: -1.3 }
    ...
ListElement{ xPos: -3.4 ; yPos: 0.6 ; zPos: 0.9 }
}
ListModel {
id: dataModelThree
ListElement{ xPos: 8.0; yPos: -2.0; zPos: 4.0 }
    ...
ListElement{ xPos: 5.66 ; yPos: -4.98 ; zPos: 3.72 }
}
					

and exposed to the other files using property alias es

property alias model: dataModel
property alias modelTwo: dataModelTwo
property alias modelThree: dataModelThree
					

Before these aliases are used, create a Scatter3DSeries type, containing a ItemModelScatterDataProxy 项。

Scatter3DSeries {
    id: scatterSeries
    itemLabelFormat: "Series 1: X:@xLabel Y:@yLabel Z:@zLabel"
    ItemModelScatterDataProxy {
        itemModel: seriesData.model
        xPosRole: "xPos"
        yPosRole: "yPos"
        zPosRole: "zPos"
    }
}
					

This data proxy refers to the datasets in Data.qml using its itemModel property. The aliases mentioned above are accessed through the id 数据 type instantiated in main.qml , seriesData .

itemModel: seriesData.model
					

Repeat this for the remaining two datasets.

Scatter3DSeries {
    id: scatterSeriesTwo
    ...
ItemModelScatterDataProxy {
    itemModel: seriesData.modelTwo
    ...
}
}
Scatter3DSeries {
id: scatterSeriesThree
    ...
ItemModelScatterDataProxy {
    itemModel: seriesData.modelThree
    ...
}
}
					

Great, the scatter graph is now pointing to the three datasets.

The only thing left to do is to add the remaining button, the theme button , to our ColumnLayout . The only difference with this button is it is positioned on the left-hand side of the layout. This is set by defining the Layout.alignment 特性到 Qt.AlignLeft and Layout.fillWidth to false .

CustomButton {
    id: themeButton
    Layout.alignment: Qt.AlignLeft
    Layout.fillWidth: false
					

范例内容

范例工程 @ code.qt.io