mirror of https://github.com/qt/qtgraphs.git
Support selectionQueryPosition and graphPositionQuery
selectionQuery is essentially a legacy way to do picking while graphPositionQuery returns a picked position of the graph. Task-number: QTBUG-138828 Pick-to: 6.10 6.8 Change-Id: Ieb90086e0416558bb31633a2b28db37443dd6362 Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
This commit is contained in:
parent
f84d80120f
commit
47dc05aac6
|
|
@ -821,7 +821,11 @@ QQuickGraphsItem::QQuickGraphsItem(QQuickItem *parent)
|
|||
connect(m_scene,
|
||||
&Q3DScene::graphPositionQueryChanged,
|
||||
this,
|
||||
&QQuickGraphsItem::handleQueryPositionChanged);
|
||||
&QQuickGraphsItem::handleGraphQueryPositionChanged);
|
||||
connect(m_scene,
|
||||
&Q3DScene::selectionQueryPositionChanged,
|
||||
this,
|
||||
&QQuickGraphsItem::handleSelectionQueryPositionChanged);
|
||||
connect(m_scene, &Q3DScene::primarySubViewportChanged,
|
||||
this,
|
||||
&QQuickGraphsItem::handlePrimarySubViewportChanged);
|
||||
|
|
@ -1070,7 +1074,7 @@ void QQuickGraphsItem::handleRequestShadowQuality(QtGraphs3D::ShadowQuality qual
|
|||
setShadowQuality(quality);
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::handleQueryPositionChanged(QPoint position)
|
||||
void QQuickGraphsItem::handleGraphQueryPositionChanged(QPoint position)
|
||||
{
|
||||
QVector3D data = graphPositionAt(position);
|
||||
setGraphPositionQueryPending(false);
|
||||
|
|
@ -1078,6 +1082,11 @@ void QQuickGraphsItem::handleQueryPositionChanged(QPoint position)
|
|||
emit queriedGraphPositionChanged(data);
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::handleSelectionQueryPositionChanged(QPoint position)
|
||||
{
|
||||
doPicking(position);
|
||||
}
|
||||
|
||||
void QQuickGraphsItem::handlePrimarySubViewportChanged(const QRect rect)
|
||||
{
|
||||
m_primarySubView = rect;
|
||||
|
|
|
|||
|
|
@ -663,7 +663,8 @@ protected:
|
|||
virtual void createSliceView();
|
||||
QQuick3DViewport *createOffscreenSliceView(QtGraphs3D::SliceCaptureType sliceType);
|
||||
|
||||
void handleQueryPositionChanged(QPoint position);
|
||||
void handleGraphQueryPositionChanged(QPoint position);
|
||||
void handleSelectionQueryPositionChanged(QPoint position);
|
||||
|
||||
void handlePrimarySubViewportChanged(const QRect rect);
|
||||
void handleSecondarySubViewportChanged(const QRect rect);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtGraphs
|
||||
import QtTest 1.1
|
||||
|
||||
Item {
|
||||
id: top
|
||||
height: 150
|
||||
width: 150
|
||||
|
||||
Scatter3D {
|
||||
id: invalid
|
||||
height: 150
|
||||
width: 150
|
||||
Scatter3DSeries {
|
||||
ItemModelScatterDataProxy {
|
||||
itemModel: ListModel {
|
||||
ListElement {x:"0"; y:"0"; z:"0"}
|
||||
ListElement {x:"0"; y:"0"; z:"1"}
|
||||
ListElement {x:"1"; y:"0"; z:"0"}
|
||||
ListElement {x:"1"; y:"0"; z:"1"}
|
||||
}
|
||||
xPosRole: "x"
|
||||
yPosRole: "y"
|
||||
zPosRole: "z"
|
||||
}
|
||||
itemSize: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
Scatter3D {
|
||||
id: zero
|
||||
anchors.fill: parent
|
||||
Scatter3DSeries {
|
||||
ItemModelScatterDataProxy {
|
||||
itemModel: ListModel {
|
||||
ListElement {x:"0"; y:"0"; z:"0"}
|
||||
ListElement {x:"0"; y:"0"; z:"1"}
|
||||
ListElement {x:"1"; y:"0"; z:"0"}
|
||||
ListElement {x:"1"; y:"0"; z:"1"}
|
||||
}
|
||||
xPosRole: "x"
|
||||
yPosRole: "y"
|
||||
zPosRole: "z"
|
||||
}
|
||||
itemSize: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
Scatter3D {
|
||||
id: valid
|
||||
height: 150
|
||||
width: 150
|
||||
property int middleX: width * 0.5
|
||||
property int middleY: height * 0.5
|
||||
measureFps: true // force rendering
|
||||
|
||||
Scatter3DSeries {
|
||||
ItemModelScatterDataProxy {
|
||||
itemModel: ListModel {
|
||||
ListElement {xPos:"0"; yPos:"0"; zPos:"0"}
|
||||
}
|
||||
xPosRole: "xPos"
|
||||
yPosRole: "yPos"
|
||||
zPosRole: "zPos"
|
||||
}
|
||||
itemSize: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Scene invalid query"
|
||||
function test_invalid_query() {
|
||||
waitForRendering(top)
|
||||
compare(invalid.scene.graphPositionQuery, Qt.point(-1, -1))
|
||||
compare(invalid.queriedGraphPosition, Qt.vector3d(0, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Scene zero query"
|
||||
function test_zero_query() {
|
||||
zero.scene.graphPositionQuery = Qt.point(0, 0)
|
||||
waitForRendering(top)
|
||||
compare(zero.scene.graphPositionQuery, Qt.point(0, 0))
|
||||
compare(zero.queriedGraphPosition, Qt.vector3d(0, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Scene valid query"
|
||||
when: valid.currentFps > 0
|
||||
function test_valid_query() {
|
||||
valid.scene.graphPositionQuery = Qt.point(valid.middleX + 1, valid.middleY)
|
||||
waitForPolish(top)
|
||||
waitForRendering(top)
|
||||
compare(graphPositionQuerySpy.count, 1)
|
||||
compare(valid.scene.graphPositionQuery, Qt.point(valid.middleX + 1, valid.middleY))
|
||||
verify(valid.queriedGraphPosition.fuzzyEquals(Qt.vector3d(2.68778, 0, 233.333)))
|
||||
}
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: graphPositionQuerySpy
|
||||
target: valid.scene
|
||||
signalName: "graphPositionQueryChanged"
|
||||
}
|
||||
}
|
||||
|
|
@ -17,14 +17,18 @@ int main(int argc, char **argv)
|
|||
tst_qmltest skip;
|
||||
return QTest::qExec(&skip, argc, argv);
|
||||
}
|
||||
#ifdef Q_OS_QNX
|
||||
if (qEnvironmentVariable("QTEST_ENVIRONMENT").split(' ').contains("ci") &&
|
||||
qEnvironmentVariable("QT_QPA_PLATFORM").split(' ').contains("offscreen")
|
||||
) {
|
||||
qWarning("This test would fail on CI QNX QEMU without OpenGL support, so it will be skipped.");
|
||||
qWarning("This test would fail on CI on offscreen test targets, so it will be skipped.");
|
||||
tst_qmltest skip;
|
||||
return QTest::qExec(&skip, argc, argv);
|
||||
}
|
||||
#ifdef Q_OS_VXWORKS
|
||||
qWarning("This test would fail due to VxWorks QtQuick3D support shortcomings, so it will "
|
||||
"be skipped.");
|
||||
tst_qmltest skip;
|
||||
return QTest::qExec(&skip, argc, argv);
|
||||
#endif
|
||||
QTEST_SET_MAIN_SOURCE_PATH
|
||||
return quick_test_main(argc, argv, "qmltest", QUICK_TEST_SOURCE_DIR);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Item {
|
|||
|
||||
property real maxSegmentCount: 10
|
||||
property real minSegmentCount: 1
|
||||
property var activeGraph: scatterGraph
|
||||
|
||||
Data {
|
||||
id: graphData
|
||||
|
|
@ -25,8 +26,97 @@ Item {
|
|||
width: parent.width
|
||||
height: parent.height - buttonLayout.height
|
||||
|
||||
Surface3D {
|
||||
id: surfaceGraph
|
||||
width: dataView.width
|
||||
height: dataView.height
|
||||
visible: activeGraph == surfaceGraph
|
||||
theme: GraphsTheme { theme: GraphsTheme.Theme.QtGreen }
|
||||
shadowQuality: Graphs3D.ShadowQuality.Medium
|
||||
cameraYRotation: 30.0
|
||||
|
||||
Surface3DSeries {
|
||||
id: surfaceSeries
|
||||
itemLabelFormat: "One - X:@xLabel Y:@yLabel Z:@zLabel"
|
||||
mesh: Abstract3DSeries.Mesh.Cube
|
||||
|
||||
|
||||
ItemModelSurfaceDataProxy {
|
||||
itemModel : ListModel {
|
||||
id: surfaceDataModel
|
||||
ListElement{ coords: "0,0"; data: "20.0/10.0/4.75"; }
|
||||
ListElement{ coords: "1,0"; data: "21.1/10.3/3.00"; }
|
||||
ListElement{ coords: "2,0"; data: "22.5/10.7/1.24"; }
|
||||
ListElement{ coords: "3,0"; data: "24.0/10.5/2.53"; }
|
||||
ListElement{ coords: "0,1"; data: "20.2/11.2/3.55"; }
|
||||
ListElement{ coords: "1,1"; data: "21.3/11.5/3.03"; }
|
||||
ListElement{ coords: "2,1"; data: "22.6/11.7/3.46"; }
|
||||
ListElement{ coords: "3,1"; data: "23.4/11.5/4.12"; }
|
||||
ListElement{ coords: "0,2"; data: "20.2/12.3/3.37"; }
|
||||
ListElement{ coords: "1,2"; data: "21.1/12.4/2.98"; }
|
||||
ListElement{ coords: "2,2"; data: "22.5/12.1/3.33"; }
|
||||
ListElement{ coords: "3,2"; data: "23.3/12.7/3.23"; }
|
||||
ListElement{ coords: "0,3"; data: "20.7/13.3/5.34"; }
|
||||
ListElement{ coords: "1,3"; data: "21.5/13.2/4.54"; }
|
||||
ListElement{ coords: "2,3"; data: "22.4/13.6/4.65"; }
|
||||
ListElement{ coords: "3,3"; data: "23.2/13.4/6.67"; }
|
||||
ListElement{ coords: "0,4"; data: "20.6/15.0/6.01"; }
|
||||
ListElement{ coords: "1,4"; data: "21.3/14.6/5.83"; }
|
||||
ListElement{ coords: "2,4"; data: "22.5/14.8/7.32"; }
|
||||
ListElement{ coords: "3,4"; data: "23.7/14.3/6.90"; }
|
||||
|
||||
ListElement{ coords: "0,0"; data: "40.0/30.0/14.75"; }
|
||||
ListElement{ coords: "1,0"; data: "41.1/30.3/13.00"; }
|
||||
ListElement{ coords: "2,0"; data: "42.5/30.7/11.24"; }
|
||||
ListElement{ coords: "3,0"; data: "44.0/30.5/12.53"; }
|
||||
ListElement{ coords: "0,1"; data: "40.2/31.2/13.55"; }
|
||||
ListElement{ coords: "1,1"; data: "41.3/31.5/13.03"; }
|
||||
ListElement{ coords: "2,1"; data: "42.6/31.7/13.46"; }
|
||||
ListElement{ coords: "3,1"; data: "43.4/31.5/14.12"; }
|
||||
ListElement{ coords: "0,2"; data: "40.2/32.3/13.37"; }
|
||||
ListElement{ coords: "1,2"; data: "41.1/32.4/12.98"; }
|
||||
ListElement{ coords: "2,2"; data: "42.5/32.1/13.33"; }
|
||||
ListElement{ coords: "3,2"; data: "43.3/32.7/13.23"; }
|
||||
ListElement{ coords: "0,3"; data: "40.7/33.3/15.34"; }
|
||||
ListElement{ coords: "1,3"; data: "41.5/33.2/14.54"; }
|
||||
ListElement{ coords: "2,3"; data: "42.4/33.6/14.65"; }
|
||||
ListElement{ coords: "3,3"; data: "43.2/33.4/16.67"; }
|
||||
ListElement{ coords: "0,4"; data: "40.6/35.0/16.01"; }
|
||||
ListElement{ coords: "1,4"; data: "41.3/34.6/15.83"; }
|
||||
ListElement{ coords: "2,4"; data: "42.5/34.8/17.32"; }
|
||||
ListElement{ coords: "3,4"; data: "43.7/34.3/16.90"; }
|
||||
|
||||
}
|
||||
|
||||
rowRole: "coords"
|
||||
columnRole: "coords"
|
||||
xPosRole: "data"
|
||||
zPosRole: "data"
|
||||
yPosRole: "data"
|
||||
rowRolePattern: /(\d),\d/
|
||||
columnRolePattern: /(\d),(\d)/
|
||||
xPosRolePattern: /^([asd]*)([fgh]*)([jkl]*)[^\/]*\/([^\/]*)\/.*$/
|
||||
yPosRolePattern: /^([^\/]*)\/([^\/]*)\/(.*)$/
|
||||
zPosRolePattern: /^([asd]*)([qwe]*)([tyu]*)([fgj]*)([^\/]*)\/[^\/]*\/.*$/
|
||||
rowRoleReplace: "\\1"
|
||||
columnRoleReplace: "\\2"
|
||||
xPosRoleReplace: "\\4"
|
||||
yPosRoleReplace: "\\3"
|
||||
zPosRoleReplace: "\\5"
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
surfaceGraph.unsetDefaultInputHandler();
|
||||
}
|
||||
|
||||
onQueriedGraphPositionChanged:
|
||||
console.log("Queried Position : " + queriedGraphPosition)
|
||||
}
|
||||
|
||||
Scatter3D {
|
||||
id: scatterGraph
|
||||
visible: activeGraph == scatterGraph
|
||||
width: dataView.width
|
||||
height: dataView.height
|
||||
theme: GraphsTheme { theme: GraphsTheme.Theme.QtGreen }
|
||||
|
|
@ -88,7 +178,7 @@ Item {
|
|||
|
||||
onWheel: (wheel)=> {
|
||||
// Adjust zoom level based on what zoom range we're in.
|
||||
var zoomLevel = scatterGraph.zoomLevel;
|
||||
var zoomLevel = activeGraph.zoomLevel;
|
||||
if (zoomLevel > 100)
|
||||
zoomLevel += wheel.angleDelta.y / 12.0;
|
||||
else if (zoomLevel > 50)
|
||||
|
|
@ -100,11 +190,11 @@ Item {
|
|||
else if (zoomLevel < 10)
|
||||
zoomLevel = 10;
|
||||
|
||||
scatterGraph.zoomLevel = zoomLevel;
|
||||
activeGraph.zoomLevel = zoomLevel;
|
||||
}
|
||||
onClicked: {
|
||||
console.log("Queried at: " + Qt.point(mouseX, mouseY))
|
||||
scatterGraph.scene.graphPositionQuery = Qt.point(mouseX, mouseY)
|
||||
activeGraph.scene.graphPositionQuery = Qt.point(mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,14 +204,14 @@ Item {
|
|||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
scatterGraph.scene.selectionQueryPosition = Qt.point(-1, -1);
|
||||
scatterGraph.scene.selectionQueryPosition = Qt.point(inputArea.mouseX, inputArea.mouseY);
|
||||
activeGraph.scene.selectionQueryPosition = Qt.point(-1, -1);
|
||||
activeGraph.scene.selectionQueryPosition = Qt.point(inputArea.mouseX, inputArea.mouseY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: cameraAnimationX
|
||||
id: scatterCameraAnimationX
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
target: scatterGraph
|
||||
|
|
@ -130,9 +220,19 @@ Item {
|
|||
to: 360.0
|
||||
duration: 20000
|
||||
}
|
||||
NumberAnimation {
|
||||
id: surfaceCameraAnimationX
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
target: surfaceGraph
|
||||
property:"cameraXRotation"
|
||||
from: 0.0
|
||||
to: 360.0
|
||||
duration: 20000
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: cameraAnimationY
|
||||
id: scatterCameraAnimationY
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
|
||||
|
|
@ -154,6 +254,29 @@ Item {
|
|||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: surfaceCameraAnimationY
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
|
||||
NumberAnimation {
|
||||
target: surfaceGraph
|
||||
property:"cameraYRotation"
|
||||
from: 5.0
|
||||
to: 45.0
|
||||
duration: 9000
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: surfaceGraph
|
||||
property:"cameraYRotation"
|
||||
from: 45.0
|
||||
to: 5.0
|
||||
duration: 9000
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttonLayout
|
||||
|
|
@ -165,14 +288,14 @@ Item {
|
|||
Button {
|
||||
id: shadowToggle
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumWidth: parent.width / 3 // 3 buttons divided equally in the layout
|
||||
Layout.minimumWidth: parent.width / 4 // 4 buttons divided equally in the layout
|
||||
text: "Hide Shadows"
|
||||
onClicked: {
|
||||
if (scatterGraph.shadowQuality === Graphs3D.ShadowQuality.None) {
|
||||
scatterGraph.shadowQuality = Graphs3D.ShadowQuality.Medium;
|
||||
if (activeGraph.shadowQuality === Graphs3D.ShadowQuality.None) {
|
||||
activeGraph.shadowQuality = Graphs3D.ShadowQuality.Medium;
|
||||
text = "Hide Shadows";
|
||||
} else {
|
||||
scatterGraph.shadowQuality = Graphs3D.ShadowQuality.None;
|
||||
activeGraph.shadowQuality = Graphs3D.ShadowQuality.None;
|
||||
text = "Show Shadows";
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +304,7 @@ Item {
|
|||
Button {
|
||||
id: cameraToggle
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumWidth: parent.width / 3
|
||||
Layout.minimumWidth: parent.width / 4
|
||||
text: "Pause Camera"
|
||||
|
||||
onClicked: {
|
||||
|
|
@ -195,10 +318,23 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: graphToggle
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumWidth: parent.width / 4
|
||||
text: "Switch graph type"
|
||||
onClicked : {
|
||||
if (activeGraph == scatterGraph)
|
||||
activeGraph = surfaceGraph
|
||||
else
|
||||
activeGraph = scatterGraph
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: exitButton
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumWidth: parent.width / 3
|
||||
Layout.minimumWidth: parent.width / 4
|
||||
text: "Quit"
|
||||
onClicked: Qt.quit();
|
||||
}
|
||||
|
|
@ -224,7 +360,7 @@ Item {
|
|||
to: mainView.maxSegmentCount
|
||||
value: 5
|
||||
|
||||
onValueChanged: scatterGraph.axisX.segmentCount = value
|
||||
onValueChanged: activeGraph.axisX.segmentCount = value
|
||||
}
|
||||
|
||||
Slider {
|
||||
|
|
@ -233,7 +369,7 @@ Item {
|
|||
to: mainView.maxSegmentCount
|
||||
value: 1
|
||||
|
||||
onValueChanged: scatterGraph.axisX.subSegmentCount = value
|
||||
onValueChanged: activeGraph.axisX.subSegmentCount = value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +386,7 @@ Item {
|
|||
to: mainView.maxSegmentCount
|
||||
value: 5
|
||||
|
||||
onValueChanged: scatterGraph.axisY.segmentCount = value
|
||||
onValueChanged: activeGraph.axisY.segmentCount = value
|
||||
}
|
||||
|
||||
Slider {
|
||||
|
|
@ -259,7 +395,7 @@ Item {
|
|||
to: mainView.maxSegmentCount
|
||||
value: 1
|
||||
|
||||
onValueChanged: scatterGraph.axisY.subSegmentCount = value
|
||||
onValueChanged: activeGraph.axisY.subSegmentCount = value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +411,7 @@ Item {
|
|||
to: mainView.maxSegmentCount
|
||||
value: 5
|
||||
|
||||
onValueChanged: scatterGraph.axisZ.segmentCount = value
|
||||
onValueChanged: activeGraph.axisZ.segmentCount = value
|
||||
}
|
||||
|
||||
Slider {
|
||||
|
|
@ -284,7 +420,7 @@ Item {
|
|||
to: mainView.maxSegmentCount
|
||||
value: 1
|
||||
|
||||
onValueChanged: scatterGraph.axisZ.subSegmentCount = value
|
||||
onValueChanged: activeGraph.axisZ.subSegmentCount = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue