2013-07-29 12 views
11

Vorrei localizzare una posizione globale di un oggetto (o relativo ad uno dei suoi antenati) e collegarlo ad una posizione di un altro oggetto.QML - rilevamento posizione globale di un componente

Stavo pensando di usare mapFromItem come segue:

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

Il problema di questo approccio è che il mapFromItem viene valutata una volta e non aggiorna come uno dei suoi argomenti viene aggiornata. Inoltre la mappatura a volte restituisce la nuova posizione modificata da un offset che non sono in grado di tracciare nel codice (ma non è questo il caso).

La mia seconda idea era di calcolare la posizione globale implementando una funzione che sommasse ricorsivamente gli offset, fermandosi all'antenato fornito (qualcosa come calculateOffsetFrom(ancestor)). Tuttavia questa è solo una funzione e, per quanto mi riguarda, non verrà rivalutata come una delle modifiche alla posizione degli antenati (a meno che, in quella funzione, legherò la chiamata al segnale onXChanged per ognuno di gli antenati lungo la strada, che sembra una soluzione sporca).

proprietà Così alla fine ho aggiunto all'oggetto ho intenzione di tenere traccia e poi mi legano a loro:

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 
} 

Ma bene ... sì ... questo non è scalabile a tutto ed è così brutto come si arriva.

Qualcuno ha idea di come questo problema possa essere risolto in modo più pulito?

Modifica: Per quanto mi riguarda, non posso utilizzare ancore in questo caso. Il componente SomeObject è un componente personalizzato che disegna una curva di Bezier da un punto a un altro (collegherà due TrackedObjects). Per questo ho bisogno della differenza tra le coordinate. Se sono corretto, le ancore non forniscono alcun modo per calcolare la distanza tra loro.

risposta

5

Questo è un punto difficile, ma qui c'è l'hack che ho usato in uno dei miei progetti: creare un rettangolo blu che è in un altro genitore rispetto al movimento rettilineo verde, rimanere allineato con esso, quando il rect verde si sposta ma anche quando rect giallo (rect genitore verde) si sposta:

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

il trucco è quello di portare tutte le coordinate di nuovo al primo antenato comune, utilizzando sia mapfromItem e mapToItem, e per forzare la funzione di essere ri-valutati, basta mettere un bandiera booleana globale che passi alla funzione di calcolo, e che inverti ogni volta che un elemento mobile sulla tua mappa si muove ... Non devi metterlo ovunque, solo sui genitori di oggetti che possono muoversi e sono dentro elemento antenato.

Quindi funziona, le tue posizioni avranno sempre ragione, ed è abbastanza scalabile e non aggiunge molto codice.

+0

Thanks a lot. Non avrei mai pensato di usare quella bandiera globale per forzare la rivalutazione. Questo sembra sporco ma fa il trucco. –

1

Io non so se questo aiuterà, ma per il rect gialla sopra, blu rect e problema rect verde menzionato da TheBootroo, ho usato il codice qui sotto per risolvere il problema

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

       ) 
    } 


} 

L'idea è per calcolare la posizione del rettangolo blu rispetto al rettangolo verde, per calcolando la posizione del rettangolo verde e dei suoi antenati visivi.

L'ispirazione alla base di questa soluzione è ->http://developer.nokia.com/Community/Wiki/How_to_create_a_Context_Menu_with_QML

+0

Simile alla soluzione rect colorata sopra menzionato, è possibile associare le coordinate xey di TrackedObject a JavaScript funzioni che attraversano l'albero visuale e restituire le coordinate dei progenitori. – Programmer

0

ho cercato di migliorare @ risposta di Shubhanga un po 'spostando il codice in un proprio file 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; 
        } 

        ) 
    } 
} 

il codice può ora essere aggiunto a qualsiasi oggetto QML come questo:

ItemPositionTracker { 
    trackedItem: rectGreen 
    movedItem: rectBlue 
} 

il che rende rectBlue seguire rectGreen.

2

La soluzione qui di seguito attiverà il qmlElementToTrack.onPropertyNameXChanged() e qmlElementToTrack.onPropertyNameYChanged() eventi ogni volta che uno dei suoi genitori 'x' o modificare i valori 'Y'.

Lo fa legandosi a di ciascun genitore onXChanged() e onYChanged() segnali. Quando uno di questi valori modifiche, si ricalcola il propertyNameX o propertyNameY valori attraversando tutti i di qmlElementToTrack genitori.

Per rendere più 'relativa' posizionato (invece di 'assoluto'), aggiungere corrente! == qmlElementToStopAt a ciascuna condizione 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

Molto bello. Una nota: oltre a collegare a '' onXChanged' e onYChanged' dovrebbe probabilmente anche collegarsi a 'onParentChanged' in modo che ogni inganno ri-genitorialità è anche rintracciato. – Russ

Problemi correlati