QML - отслеживание глобальной позиции компонента

Я хотел бы отслеживать глобальную позицию объекта (или относительно одного из его предков) и привязывать его к некоторой позиции позиции.

Я думал об использовании 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
}

Но хорошо... да... этот не совсем масштабируется и так же уродлив, как и он.

Кто-нибудь знает, как эта проблема может быть решена более чистым способом?

Edit: Что касается меня, я не могу использовать якоря в этом случае. Компонент SomeObject представляет собой настраиваемый компонент, который рисует кривую Безье из одной точки в другую (она соединяет два TrackedObjects). Для этого мне нужна разница между координатами. Если я верный якорь не предоставляет никакого способа расчета расстояния между ними.

Ответ 1

Это трудный момент, но вот хак, который я использовал в одном из моих проектов: сделать синий прямоугольник, который находится в другом родительском, чем зеленый прямоугольный ход, оставаться в нем, при движении зеленого прямоугольника, а также при желтом rect (зеленый прямой родительский) перемещается:

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, и чтобы заставить функцию переоценивать, просто поместите глобальный логический флаг, который вы передаете вычислительной функции, и что вы инвертируете каждый раз, когда перемещаемый элемент на вашей карте перемещается... Вам не нужно класть его куда угодно, только на родителей предметов, которые могут перемещаться и находятся внутри элемента предка.

Итак, это работает, ваши позиции всегда будут правы, и он достаточно масштабируемый и не добавляет много кода.

Ответ 2

В приведенном ниже решении будут возникать события qmlElementToTrack.onPropertyNameXChanged() и qmlElementToTrack.onPropertyNameYChanged() каждый раз, когда изменяются значения одного из его родителей x или y.

Он делает это, привязывая к каждому родительскому сигналу onXChanged() и onYChanged(). Когда одно из этих значений изменяется, оно пересчитывает значения свойстваNameX или propertyNameY, перемещая всех родителей qmlElementToTrack.

Чтобы сделать его "относительным" позиционированным (вместо "абсолютного" ), добавьте текущий! == qmlElementToStopAt к каждому условию while().

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

Ответ 3

Я не знаю, поможет ли это, но для вышеупомянутой проблемы с желтым прямоугольником, синей прямой и зеленой прямой, упомянутой TheBootroo, я использовал приведенный ниже код для решения проблемы.

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

Ответ 4

Я попытался улучшить ответ @Shubhanga, переместив код в свой собственный файл ItemPositionTracker.qml:

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.

Ответ 5

Отслеживание определенных глобальных позиций Предмета кажется важной проблемой при разработке сложных графических взаимодействий. Я придумал относительно простое и изящное решение. Вот мои основные коды:

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");
    }
}

Основная идея заключается в следующем: что по сути делает глобальное изменение позиции Предмета? Возможно, он сам, его родитель или родительский родитель и т.д. Так что сделайте обратный ход к самому дальнему родителю и подключите каждый из сигналов изменения его/ее предка к функции, в рамках которой мы получаем глобальную позицию элемента и транслируем за пределы.