17

我想跟踪一个对象的全局位置(或相对于它的一个祖先)并将其绑定到其他项目的位置。

我正在考虑使用mapFromItem如下:

SomeObject {
  x: ancestor.mapFromItem(trackedObject, trackedObject.x, 0).x
  y: ancestor.mapFromItem(trackedObject, 0, trackedObject.y).y
}

这种方法的问题是mapFromItem被评估一次并且不会随着其中一个参数的更新而更新。此外,映射有时会返回由我无法在代码中跟踪的偏移量更改的新位置(但这不是手头的问题)。

我的第二个想法是通过实现一个函数来计算全局位置,该函数将递归地求和偏移量,在提供的祖先处停止(类似于calculateOffsetFrom(ancestor))。仍然这只是一个函数,就我而言,它不会随着祖先位置的变化而重新评估(除非在该函数中,我会将调用它绑定到onXChanged每个一路上的祖先,这似乎是一个肮脏的解决方案)。

所以最后我为我打算跟踪的对象添加了属性,然后绑定到它们:

TrackedObject {
  property real offsetX: x + parent.x + parent.parent.x + parent.parent.parent.x ...
  property real offsetY: y + parent.y + parent.parent.y + parent.parent.parent.y ...
}

SomeObject {
  x: trackedObject.globalX
  y: trackedObject.globalY
}

但是好吧......是的......这个根本没有规模,而且很丑陋。

有谁知道如何以更清洁的方式解决这个问题?

编辑: 就我而言,在这种情况下我不能使用锚。该SomeObject组件是一个自定义组件,从一个点到另一个点绘制一条贝塞尔曲线(它将连接两个TrackedObjects)。为此,我需要坐标之间的差异。如果我是正确的,锚不提供任何计算它们之间距离的方法。

4

5 回答 5

11

这是一个难点,但这是我在我的一个项目中使用的技巧:使在另一个父级中的蓝色矩形而不是绿色矩形移动,与它保持对齐,当绿色矩形移动时以及黄色矩形(绿色rect parent) 移动:

import QtQuick 2.0;

Rectangle {
    id: window;
    width: 800;
    height: 480;

    property bool globalBit : true;

    function updatePos (item_orig, item_dest, bit) {
        var pos_abs = window.mapFromItem (item_orig.parent, item_orig.x, item_orig.y);
        return window.mapToItem (item_dest.parent, pos_abs.x, pos_abs.y);
    }

    Rectangle {
        id: rectYellow;
        width: 400;
        height: 300;
        x: 300;
        y: 200;
        color: "yellow";

        onXChanged: { globalBit = !globalBit; }
        onYChanged: { globalBit = !globalBit; }

        MouseArea {
            drag {
                target: rectYellow;
                minimumX: 0;
                minimumY: 0;
                maximumX: (rectYellow.parent.width - rectYellow.width);
                maximumY: (rectYellow.parent.height - rectYellow.height);
            }
            anchors.fill: parent;
        }
        Rectangle {
            id: rectGreen;
            x: 100;
            y: 100;
            width: 50;
            height: 50;
            color: "green";

            MouseArea {
                drag {
                    target: rectGreen;
                    minimumX: 0;
                    minimumY: 0;
                    maximumX: (rectGreen.parent.width - rectGreen.width);
                    maximumY: (rectGreen.parent.height - rectGreen.height);
                }
                anchors.fill: parent;
            }
        }
    }
    Rectangle {
        id: rectBlue;
        x: pos.x + 50;
        y: pos.y + 50;
        width: 50;
        height: 50;
        color: "blue";

        property var pos : updatePos (rectGreen, rectBlue, globalBit);
    }
}

诀窍是使用 mapfromItem 和 mapToItem 将所有坐标带回第一个共同祖先,并强制重新评估函数,只需放置一个传递给计算函数的全局布尔标志,然后反转每个地图上的可移动元素移动的时间......您不必将它放在任何地方,只需放在可以移动且位于祖先项目内的项目的父项上。

所以它起作用了,你的位置总是正确的,而且它的可扩展性很强,不会添加太多代码。

于 2013-08-08T10:20:30.283 回答
2

以下解决方案将触发qmlElementToTrack.onPropertyNameXChanged()qmlElementToTrack.onPropertyNameYChanged()事件,每次其父 'x' 或 'y' 值之一发生变化时。

它通过附加到每个父母的onXChanged()onYChanged()信号来做到这一点。当其中一个值发生变化时,它会通过遍历所有qmlElementToTrack 的父项来重新计算propertyNameXpropertyNameY值。

要使其“相对”定位(而不是“绝对”),请将current !== qmlElementToStopAt添加到每个 while() 条件。

ElementToTrack {
  id: qmlElementToTrack
  property real propertyNameX: 0
  property real propertyNameY: 0
}

setPositionChangedToParents(qmlElementToTrack);


/**
  Connect to each parent's 'onXChanged' and 'onYChanged' signals.
*/
setPositionChangedToParents = function(current) {
    while (current && current.parent) {
        current.onXChanged.connect(calculatePropertyNameX);
        current.onYChanged.connect(calculatePropertyNameY);
        current = current.parent;
    }
};


/**
  Disconnects the signals set to all parents.
*/
removePositionChangedFromParents = function(current) {
    while (current && current.parent) {
        current.onXChanged.disconnect(calculatePropertyNameX);
        current.onYChanged.disconnect(calculatePropertyNameY);
        current = current.parent;
    }
};


/**
  When any parent's 'x' changes, recalculate the 'x' value for the 'property name'.
*/
calculatePropertyNameX = function() {
    var calculatedX, current;

    calculatedX = 0;
    current = qmlElementToTrack;

    while (current && current.parent) {
        calculatedX += current.x;
        current = current.parent;
    }

    propertyNameX = calculatedX;
};


/**
  When any parent's 'y' changes, recalculate the 'y' value for the 'property name'.
*/
calculatePropertyNameY = function() {
    var calculatedY, current;

    calculatedY = 0;
    current = qmlElementToTrack;

    while (current && current.parent) {
        calculatedY += current.y;
        current = current.parent;
    }

    propertyNameY = calculatedY;
};

于 2015-04-09T18:05:56.267 回答
1

我不知道这是否会有所帮助,但是对于 TheBootro 提到的上述黄色矩形,蓝色矩形和绿色矩形问题,我使用以下代码解决了问题

import QtQuick 2.0;

Rectangle {
id: window;
width: 800;
height: 480;


Rectangle {
    id: rectYellow;
    width: 400;
    height: 300;
    x: 300;
    y: 200;
    color: "yellow";

    MouseArea {
        drag {
            target: rectYellow;
            minimumX: 0;
            minimumY: 0;
            maximumX: (rectYellow.parent.width - rectYellow.width);
            maximumY: (rectYellow.parent.height - rectYellow.height);
        }
        anchors.fill: parent;
    }
    Rectangle {
        id: rectGreen;
        x: 100;
        y: 100;
        width: 50;
        height: 50;
        color: "green";

        MouseArea {
            drag {
                target: rectGreen;
                minimumX: 0;
                minimumY: 0;
                maximumX: (rectGreen.parent.width - rectGreen.width);
                maximumY: (rectGreen.parent.height - rectGreen.height);
            }
            anchors.fill: parent;
        }
    }
}
Rectangle {
    id: rectBlue;
    //Need to acheive the below behvior(commented)
    //x: window.x+rectYellow.x+rectGreen.x+50
    //y: window.y + rectYellow.y +rectGreen.y+50
    width: 50;
    height: 50;
    color: "blue";

}
Component.onCompleted: {
    rectBlue.x =Qt.binding(
                function()
                {
                     //Returns window.x+rectYellow.x+rectGreen.x+rectGreen.width
                    var docRoot = null;
                    var x=rectGreen.x;
                    if(!docRoot)
                    {
                       docRoot = rectGreen.parent;
                       x+=docRoot.x;

                       while(docRoot.parent)
                       {
                           docRoot = docRoot.parent;
                           x+=docRoot.x
                       }
                    }
                    return x+rectGreen.width;
                }

                )
    rectBlue.y = Qt.binding(
                function()
                {
                    //Returns window.y+rectYellow.y+rectGreen.y+rectGreen.height
                    var docRoot = null;
                    var y=rectGreen.y
                    if(!docRoot)
                    {
                       docRoot = rectGreen.parent;
                       y+=docRoot.y;

                       while(docRoot.parent)
                       {
                           docRoot = docRoot.parent;
                           y+=docRoot.y
                       }
                    }
                    return y+rectGreen.height;
                }

                )
    }


}

这个想法是通过计算绿色矩形及其视觉祖先的位置来计算蓝色矩形相对于绿色矩形的位置。

这个解决方案背后的灵感是-> http://developer.nokia.com/Community/Wiki/How_to_create_a_Context_Menu_with_QML

于 2013-08-12T02:57:30.127 回答
1

如果开发一些复杂的图形交互,跟踪某些项目的全局位置似乎是一个重要的问题。我想出了一个相对简单而优雅的解决方案。这是我的核心代码:

Item{
    id: globalRoot
    signal globalPositionChanged(Item item, real newX, real newY);

    function tracking(item){
        var obj = item;
        var objN;
        function onGlobalXYChanged(){
            var pt = mapFromItem(item, item.x, item.y);
            globalRoot.globalPositionChanged(item, pt.x, pt.y);
        }
        do{
            objN = obj.objectName;
            obj.xChanged.connect(onGlobalXYChanged);
            obj.yChanged.connect(onGlobalXYChanged);
            obj = obj.parent;
        }while(objN !== "furthestAncestorObjectName");
    }
}

核心思想是:本质上是什么让一个Item的全局位置发生了变化?它可能是它自己、它的父级或它的父级的父级等。因此,遍历其最远的父级,并将其祖先的每个 x/y 更改信号连接到一个函数,在该函数中我们获取项目的全局位置并在外部广播。

于 2019-07-01T07:05:09.107 回答
0

我试图通过将代码移动到它自己的 ItemPositionTracker.qml 文件中来稍微改进@Shubhanga 的答案:

import QtQuick 2.3

Item {
    id: root

    property Item trackedItem
    property Item movedItem

    Component.onCompleted: {
        movedItem.x =
                Qt.binding(
                    function()
                    {
                        if (trackedItem === null) return 0;

                        var docRoot = trackedItem;
                        var x = trackedItem.x;

                        while(docRoot.parent)
                        {
                            docRoot = docRoot.parent;
                            x += docRoot.x
                        }

                        return x;
                    }

                    )
        movedItem.y =
                Qt.binding(
                    function()
                    {
                        if (trackedItem === null) return 0;

                        var docRoot = trackedItem;
                        var y = trackedItem.y

                        while(docRoot.parent)
                        {
                            docRoot = docRoot.parent;
                            y += docRoot.y
                        }

                        return y;
                    }

                    )
    }
}

现在可以将代码添加到任何 QML 对象,如下所示:

ItemPositionTracker {
    trackedItem: rectGreen
    movedItem: rectBlue
}

这使得 rectBlue 跟随 rectGreen。

于 2014-10-09T11:48:55.930 回答