Implementing axis dragging in QML.
The Qt Quick 2 axis dragging example concentrates on showing how to implement axis range changing by dragging axis labels in QML. It also gives a quick peek to two other new features in Qt Data Visualization 1.1: orthographic projection and dynamic custom item handling.
要运行范例从 Qt Creator ,打开 欢迎 模式,然后选择范例从 范例 。更多信息,拜访 构建和运行范例 .
First we deactivate the default input handling mechanism by setting the active input handler of
Scatter3D
graph to
null
:
Scatter3D { id: scatterGraph inputHandler: null ...
Then we add a
MouseArea
and set it to fill the parent, which is the same
Item
our
scatterGraph
is contained in. We also set it to accept only left mouse button presses, as in this example we are not interested in other buttons:
MouseArea { anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.LeftButton ...
Then we need to listen to mouse presses, and when caught, send a selection query to the graph:
onPressed: { scatterGraph.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y); }
Current mouse position, that will be needed for move distance calculation, is caught in
onPositionChanged
:
onPositionChanged: { currentMouseX = mouse.x; currentMouseY = mouse.y; ...
At the end of
onPositionChanged
, we'll save the previous mouse position for move distance calculation that will be introduced later:
... previousMouseX = currentMouseX; previousMouseY = currentMouseY; }
in
scatterGraph
we will need to listen to
onSelectedElementChanged
signal. The signal is emitted after the selection query has been made in the
onPressed
of
inputArea
. We set the element type into a property we defined (
property int selectedAxisLabel: -1
) in our main component, since it is of a type we are interested in:
onSelectedElementChanged: { if (selectedElement >= AbstractGraph3D.ElementAxisXLabel && selectedElement <= AbstractGraph3D.ElementAxisZLabel) selectedAxisLabel = selectedElement else selectedAxisLabel = -1 }
Then, back in the
onPositionChanged
of
inputArea
, we check if a mouse button is pressed and if we have a current axis label selection. If the conditions are met, we'll call the function that does the conversion from mouse movement to axis range update:
... if (pressed && selectedAxisLabel != -1) dragAxis(); ...
The conversion is easy in this case, as we have a fixed camera rotation. We can use some precalculated values, calculate mouse move distance, and apply the values to the selected axis range:
function dragAxis() { // Do nothing if previous mouse position is uninitialized if (previousMouseX === -1) return // Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we // can use one precalculated value instead of calculating xx, xy, zx and zy individually var cameraMultiplier = 0.70710678 // Calculate the mouse move amount var moveX = currentMouseX - previousMouseX var moveY = currentMouseY - previousMouseY // Adjust axes switch (selectedAxisLabel) { case AbstractGraph3D.ElementAxisXLabel: var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier // Check if we need to change min or max first to avoid invalid ranges if (distance > 0) { scatterGraph.axisX.min -= distance scatterGraph.axisX.max -= distance } else { scatterGraph.axisX.max -= distance scatterGraph.axisX.min -= distance } break case AbstractGraph3D.ElementAxisYLabel: distance = moveY / dragSpeedModifier // Check if we need to change min or max first to avoid invalid ranges if (distance > 0) { scatterGraph.axisY.max += distance scatterGraph.axisY.min += distance } else { scatterGraph.axisY.min += distance scatterGraph.axisY.max += distance } break case AbstractGraph3D.ElementAxisZLabel: distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier // Check if we need to change min or max first to avoid invalid ranges if (distance > 0) { scatterGraph.axisZ.max += distance scatterGraph.axisZ.min += distance } else { scatterGraph.axisZ.min += distance scatterGraph.axisZ.max += distance } break } }
For a more sophisticated conversion from mouse movement to axis range update, see this example .
The example also demonstrates how to use orthographic projection and how to update properties of a custom item on the fly.
Orthographic projection is very simple. You'll just need to change
orthoProjection
property of
scatterGraph
. In this example we have a button for toggling it on and off:
NewButton { id: orthoToggle width: parent.width / 3 text: "Display Orthographic" anchors.left: rangeToggle.right onClicked: { if (scatterGraph.orthoProjection) { text = "Display Orthographic"; scatterGraph.orthoProjection = false // Orthographic projection disables shadows, so we need to switch them back on scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityLow } else { text = "Display Perspective"; scatterGraph.orthoProjection = true } } }
For custom items, first we'll add one in the
customItemList
of
scatterGraph
:
customItemList: [ Custom3DItem { id: qtCube meshFile: ":/mesh/cube" textureFile: ":/texture/texture" position: Qt.vector3d(0.65,0.35,0.65) scaling: Qt.vector3d(0.3,0.3,0.3) } ]
We have implemented a timer to add, remove, and rotate all the items in the graph, and we'll use the same timer for rotating the custom item:
onTriggered: { rotationAngle = rotationAngle + 1 qtCube.setRotationAxisAndAngle(Qt.vector3d(1,0,1), rotationAngle) ...