How to omit the "Binding loop detected for property" warning?
Solution 1
The solution is simply use Connections
:
import QtQuick 2.5
MultiPointTouchArea {
id: myTouchSurface
// this is object I want to respond to touch gestures
property Item target: parent
// this is object I want to keep target in. I won't let target get out of it
property Item container: parent.parent
property double targetX: target.x
property double targetY: target.y
property double centerX: target.width / 2
property double centerY: target.height / 2
property double lastTouchX
property double lastTouchY
property double lastXDrag
property double lastYDrag
// here I calculate received touches and move target
onPressed: {
lastTouchX = touchPoints[0].sceneX
lastTouchY = touchPoints[0].sceneY
if (slidingAnimation.running)
slidingAnimation.stop()
}
onTouchUpdated: {
if (touchPoints.length) {
target.x += touchPoints[0].sceneX - lastTouchX
target.y += touchPoints[0].sceneY - lastTouchY
lastXDrag = touchPoints[0].sceneX - lastTouchX
lastYDrag = touchPoints[0].sceneY - lastTouchY
lastTouchX = touchPoints[0].sceneX
lastTouchY = touchPoints[0].sceneY
}
else
startSliding()
}
// when lifting fingers off screen I want to slide target across it's container
function startSliding() {
slidingAnimation.toX = target.x + lastXDrag * 10
slidingAnimation.toY = target.y + lastYDrag * 10
slidingAnimation.restart()
}
// This is animation responsible for sliding the target
ParallelAnimation {
id: slidingAnimation
property double toX
property double toY
property int animationDuration: 250
NumberAnimation {
target: myTouchSurface.target
property: "x"
to: slidingAnimation.toX
duration: slidingAnimation.animationDuration
easing.type: Easing.OutQuad
}
NumberAnimation {
target: myTouchSurface.target
property: "y"
to: slidingAnimation.toY
duration: slidingAnimation.animationDuration
easing.type: Easing.OutQuad
}
}
Connections{
target:myTouchSurface.target
onXChanged:{
if (container != null) {
if (target.x + centerX < 0)
target.x = -centerX
else if (target.x + centerX > container.width)
target.x = container.width - centerX
}
}
onYChanged:{
if (container != null) {
if (target.y + centerY < 0)
target.y = -centerY
else if (target.y + centerY > container.height)
target.y = container.height - centerY
}
}
}
// this is how I keep target object within container
// onTargetXChanged: {
// }
// onTargetYChanged: {
// }
}
Solution 2
I think your problem is that you update either targetX
or target.x
(resp. for y
) inside of the property update handler (onTargetXChanged()
).
This might be picked up as a biding loop by the QML engine as if you change target.x
from onTargetXChanged()
the signal onTargetXChanged()
will trigger again as it's bound to it in property double targetX: target.x
.
You can try something like this:
TouchArea {
property Item target // target is the Image object being moved
property double targetX: target.x
property double targetY: target.y
onTargetXChanged: {
/* if object is out of specified area move x respectively */
performPropertyUpdate(args)
}
onTargetYChanged: {
/* if object is out of specified area move y respectively */
performPropertyUpdate(args)
}
function performPropertyUpdate(args) {
/* actual setting of target.x or target.y */
}
}
Solution 3
To solve the issue myX
should be an alias of x
,
property alias myX: rectangle2.x
You're getting this problem cause myX
is changing the value of x
and x
is changing the value of myX
: a loop.
Filip Hazubski
I do what I love. I code :) To the extent possible under law, Filip Hazubski has waived all copyright and related or neighboring rights to Stack Exchange sites comments, questions and answers written by me. This work is published from: Polska. http://creativecommons.org/publicdomain/zero/1.0/
Updated on June 30, 2022Comments
-
Filip Hazubski almost 2 years
I decided to rewrite this question. I tried to make it clear and short but it didn't work out.
What I am trying to achieve is an object I can put on anything (
Rectangle
,Image
and so on) and make it respond to touch gestures. Unfortunately I ran into a problem I cannot solve. I couldn't find help so I try here.Here is simple
main.qml
:import QtQuick 2.5 import QtQuick.Controls 1.3 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 ApplicationWindow { title: qsTr("Touch surface test") width: 640 height: 480 visible: true Rectangle { width: 200 height: 60 color: "blue" MyTouchSurface { id: myTouchSurface anchors.fill: parent } } }
and here is MyTouchSurface.qml
import QtQuick 2.5 MultiPointTouchArea { id: myTouchSurface // this is object I want to respond to touch gestures property Item target: parent // this is object I want to keep target in. I won't let target get out of it property Item container: parent.parent property double targetX: target.x property double targetY: target.y property double centerX: target.width / 2 property double centerY: target.height / 2 property double lastTouchX property double lastTouchY property double lastXDrag property double lastYDrag // here I calculate received touches and move target onPressed: { lastTouchX = touchPoints[0].sceneX lastTouchY = touchPoints[0].sceneY if (slidingAnimation.running) slidingAnimation.stop() } onTouchUpdated: { if (touchPoints.length) { target.x += touchPoints[0].sceneX - lastTouchX target.y += touchPoints[0].sceneY - lastTouchY lastXDrag = touchPoints[0].sceneX - lastTouchX lastYDrag = touchPoints[0].sceneY - lastTouchY lastTouchX = touchPoints[0].sceneX lastTouchY = touchPoints[0].sceneY } else startSliding() } // when lifting fingers off screen I want to slide target across it's container function startSliding() { slidingAnimation.toX = target.x + lastXDrag * 10 slidingAnimation.toY = target.y + lastYDrag * 10 slidingAnimation.restart() } // This is animation responsible for sliding the target ParallelAnimation { id: slidingAnimation property double toX property double toY property int animationDuration: 250 NumberAnimation { target: myTouchSurface.target property: "x" to: slidingAnimation.toX duration: slidingAnimation.animationDuration easing.type: Easing.OutQuad } NumberAnimation { target: myTouchSurface.target property: "y" to: slidingAnimation.toY duration: slidingAnimation.animationDuration easing.type: Easing.OutQuad } } // this is how I keep target object within container onTargetXChanged: { if (container != null) { if (target.x + centerX < 0) target.x = -centerX else if (target.x + centerX > container.width) target.x = container.width - centerX } } onTargetYChanged: { if (container != null) { if (target.y + centerY < 0) target.y = -centerY else if (target.y + centerY > container.height) target.y = container.height - centerY } } }
I want to have all calculating and functions within
MyTouchSurface
so it is easy to apply on other object and to use it in another project.The problem I have is I don't know how to prevent target object from moving out of container. Well, the code here is working very nice but it also generates very ugly errors about binding loop.
What is going on is:
- I check target's x and y when they changes
- If the x or y is out of specified area I move them back
- I receive signal "x changed" or "y changed" again
- I receive error about loop because my function caused itself to execute again
So I am asking: Is there any other simple way to prevent object from flying off screen and not receive binding loop errors at the same time?
I know I can use
Timer
to check target'sx
andy
from time to time, and bring them back.I know I can change the
easing
andduration
of the animation so it stops on the container bounds itself but looks like it was stopped.Seriously. Is there no simple way?
Things that won't work:
- stopping animation after object was detected outside container (if it would move really fast it will stop out of the screen )
- stopping animation and then changing target's
x
ory
Thanks everyone helping me so far. It is nice to know I am not alone here :)