2013-07-29 2 views
11

Я хотел бы отслеживать глобальную позицию объекта (или относительно одного из его предков) и привязывать его к позиции какого-либо другого элемента.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 
} 

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

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

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

ответ

5

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

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

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

+0

Спасибо большое. Не подумал бы подумать об использовании этого глобального флага для принудительной переоценки. Это кажется грязным, но это делает трюк. –

1

Я не знаю, будет ли это поможет, но для вышеприведенного желтого прямоугольника, синего и зеленого прямоугольника Rect проблемы, упомянутой 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

+0

Как и упомянутое выше решение цветного прямоугольника, вы можете привязать координаты x и y объекта TrackedObject к функциям javascript, которые пересекают визуальное дерево и возвращают координаты предков. – Programmer

0

Я пытался улучшить @ ответ 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.

2

Раствор ниже вызовет qmlElementToTrack.onPropertyNameXChanged() и qmlElementToTrack.onPropertyNameYChanged() события каждый раз, когда один из его родителей х 'или изменить «у» значения.

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

Чтобы сделать его «относительным» позиционированным (вместо «абсолютного»), добавьте 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; 
 
};

+0

Очень приятно. Одна заметка: помимо подключения к 'onXChanged' и' onYChanged', он также должен подключаться к 'onParentChanged', чтобы отслеживать любую перегруппировку. – Russ

Смежные вопросы