【问题标题】:How to omit the "Binding loop detected for property" warning?如何省略“检测到属性的绑定循环”警告?
【发布时间】:2015-10-18 16:35:00
【问题描述】:

我决定重写这个问题。我试图让它清晰而简短,但没有成功。

我想要实现的是一个可以放在任何东西上的对象(RectangleImage 等等)并让它响应触摸手势。不幸的是,我遇到了一个我无法解决的问题。我找不到帮助,所以我在这里尝试。

这里很简单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
        }
    }

}

这里是 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
        }
    }
}

我希望在MyTouchSurface 中包含所有计算和函数,以便轻松应用于其他对象并在其他项目中使用它。

我的问题是我不知道如何防止目标对象移出容器。好吧,这里的代码运行得很好,但它也会产生关于绑定循环的非常丑陋的错误。

发生了什么:

  • 当目标的 x 和 y 发生变化时,我会检查它们
  • 如果 x 或 y 超出指定区域,我会将它们移回
  • 我再次收到信号“x changed”或“y changed”
  • 我收到有关循环的错误,因为我的函数导致自己再次执行

所以我想问:有没有其他简单的方法可以防止对象飞出屏幕并且不会同时收到绑定循环错误?

我知道我可以不时使用Timer 来检查目标的xy,并将它们带回来。

我知道我可以更改动画的 easingduration,使其停在容器边界上,但看起来好像已停止。

说真的。没有简单的方法吗?


不起作用的事情:

  • 在容器外检测到对象后停止动画(如果它移动得非常快,它将停止在屏幕外)
  • 停止动画然后更改目标的xy

感谢到目前为止帮助我的所有人。很高兴知道我在这里并不孤单:)

【问题讨论】:

  • 忽略警告可能是最糟糕的决定。绑定循环提示您做错了什么,您应该进一步调查 qml 引擎在抱怨什么。您的目标应该是正确设计您的 qml 文件,这意味着:更改基于拖动交互转换项目位置的方式。为了帮助您,我们需要的不仅仅是那几行代码。
  • @qCring 我附上了简单的例子,你可以复制粘贴来重现我的问题。
  • 你确实有一个绑定循环。修理它。让我解释一下您的问题:“我知道我有问题,但我不想解决它”。如果是这样,你的问题是什么? (不,警告不能被删除,这是有充分理由的 - QML 正在保护你的脚免受你自己的伤害。)
  • @KubaOber 我就是不明白。算法是这样的:如果变量发生变化,检查它是否保持在想要的范围之间,如果不是 - 改变它。如果操作再次触发,更改它会导致相同的结果。然后出现警告。我该如何解决?那是我的问题。如果它是一个循环,那么很好,无论如何,但我真的没有看到任何简单的方法来改变它。如果我们谈论删除警告 - 请查看示例代码。相同的操作在一个地方会导致错误,而在另一个地方不会。对我来说,一切都不是那么明显。

标签: qt qml qtquick2 qt5.5


【解决方案1】:

解决方法就是使用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: {

//    }
}

【讨论】:

  • 很好。效果很好!非常感谢! :)
【解决方案2】:

我认为您的问题是您在属性更新处理程序 (onTargetXChanged()) 内部更新了 targetXtarget.x(分别为 y)。

这可能会被 QML 引擎作为投标循环拾取,就好像您将 target.xonTargetXChanged() 更改为信号 onTargetXChanged() 将再次触发,因为它在属性 double targetX: target.x 中绑定到它。

你可以试试这样的:

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 */
  }
}

【讨论】:

  • 谢谢。不幸的是,这并不能解决问题。
  • 您是否尝试过将property double myX: x 转换为property alias myX: x 或仅使用onXChanged(如果可用)?
  • 很遗憾,我无法使用onXChanged。尝试使用别名会出现此错误:Invalid alias reference. Unable to find id "target"target 仅引用另一个文件中的对象。在我的文件中有一个MultiPointTouchArea,没有别的。然后我将此文件中的对象应用于另一个对象。
  • @Mertanian 别名引用无效,因为您需要添加具有属性 target、beore target、example instance.target 的对象的 ID。看我的回答
【解决方案3】:

解决问题myX应该是x的别名,

property alias myX: rectangle2.x

您遇到此问题是因为 myX 正在更改 x 的值,而 x 正在更改 myX 的值:一个循环。

【讨论】:

  • 对不起。也许我把事情搞砸了。示例代码是为了显示错误。在实际情况下,触摸区域位于一个单独的文件中,并且我没有想要在屏幕上移动的目标的 ID。因此我不能使用别名。很快我将修改我的示例代码,以确保一切都与我在应用程序中使用的一样。
  • 好的,等你的例子:)
  • 这真的应该是您的单独答案吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-17
  • 1970-01-01
相关资源
最近更新 更多