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