How to track the global position of an Item?



  • Hi!

    I would like to track the global position of an item (relative to the root item). Property binding to the item's x and y coordinates does not help because they are relative to its parent, so mapToItem would not be reevaluated if the parent is moved.

    Maybe there's something in Qml that I've missed, so before I start working on a C++ extension that provides the information (should be possible, I guess), I'm asking here.

    Thanks!

    Nils



  • Hi Nils,

    I don't think you are missing anything: the built-in QML elements don't provide any simple ways of dealing with global position changes.

    Regards,
    Michael



  • ok, thanks. I'll look into doing this from the C++ side.



  • Hi Njeisecke,
    I think you really miss something, I use mapFromItem()/mapToItem() function to get the item's position in global scene.

    http://doc.qt.nokia.com/4.7-snapshot/qml-item.html#mapFromItem-method

    The document says:
    Maps the point (x, y), which is in item's coordinate system, to this item's coordinate system, and returns an object with x and y properties matching the mapped cooordinate.
    If item is a null value, this maps the point from the coordinate system of the root QML view.



  • Hi diro,

    the problem is that x and y do not change if for example the item's parent is being moved. So in this case mapFromItem would not be reevaluated event though the global position changes.

    Nils



  • Hi Nils,
    I think the event issue could be solved in QML, the main idea is that if the itme's coordinate won't change but its parent will, how about leverage its notify?

    The original code may be:
    subpanel.mapToItem(null, 0, 0).x
    than, becomes:
    subpanel.mapToItem(null, (basepanel.width * 0) + 0, 0).x

    PS.The basepanel.width may changed to other properties, such as x, y, height... and so on, depends on your case

    The following is my full sample code, you may try it.

    @import QtQuick 1.1

    Rectangle {
    id: basepanel
    anchors.leftMargin: 10;
    anchors.rightMargin: 10;
    width:800;
    height: 480;
    color:"#333333";

    Rectangle {
        id: subpanel
        width: 657
        height: 294
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        color:"black"
        border.color: "#777777"
    }
    
    Text {
        id: status
        x: parent.width/2
        y: parent.height/2
        color: "#ffffff"
        text: qsTr("Global Position of item subpanel:") +
              subpanel.mapToItem(null, (basepanel.width * 0) + 0, 0).x +
              "," +
              subpanel.mapToItem(null, (basepanel.width * 0) + 0, 0).y;
        visible: true
        styleColor: "#a23e3e"
    }
    

    }
    @

    Besides, if you really want to monitor many properties of basepanel, you may use
    @ (basepanel.width * basepanel.height * basepanel.x * basepanel.y * 0)@



  • Hi Diro,

    thank you very much for posting your example code.

    The problem is that "subpanel" is supposed to be a component. It doesn't know if the moving item is it's parent, parent parent oder parent parent parent, ...

    So to get it right you would have to iterate through the parent chain and bind to each x and y property. You also have to take possible reparenting into account.

    I guess you could do this in pure Qml. However I doubt that the amount of necessary bindings and function calls would give reasonable performance in more complex applications.

    So as soon as time admits, I'm going to check what could be done from the C++ side. The downside of this approach might be that I'll have to do it again for the scene graph.

    Nils



  • Hi Nils,
    Okay, I understand your problem now. And I also interested in your C++ solution, hope you can share with us in the future, thanks :-)



  • Hi,

    Did you get any progress on that ?

    Merinas



  • No, not really.

    I've been talking about this with some Qt developers at DevDays and they came to the conclusion that this would be pretty difficult to implement generally without sacrificing overall system performance.

    You should be able to connect to all xChanged and yChanged signals of the item's parent chain but I have not tried this yet. Seems to be the only possibility though.

    Nils



  • Yes, connecting an item's own x/y-Changed signals to the xChanged and yChanged signals of all parents is possible - I tried it and it works fine. But I do not recommend it for items that change often, because the performance penalty is significant! If you have control over the items you want to track, it would be the best solution to write a custom QDeclarativeItem in C++ and overwrite the itemChange function from QGraphicsItem: "QGraphicsItem itemChange function":http://doc.qt.nokia.com/4.7-snapshot/qgraphicsitem.html#itemChange.

    Regarding the doc, this also has a performance impact, but presumably not that much compared to the parent-signal-connection approach.

    Chris



  • Maybe we need a specific Item to provide this functionnality or add a new property in item to add tracking global pos.

    For now I used the flag ItemSendsScenePositionChanges in a new item. My purpose is to anchor a punctual item to another punctual item anywhere ni the QML tree here what I used :

    @class AnchorPoint : public QDeclarativeItem
    {
    Q_OBJECT
    Q_DISABLE_COPY(AnchorPoint)
    Q_PROPERTY(AnchorPoint* anchorTo READ anchorTo WRITE setAnchorTo NOTIFY anchorToChanged)

    public:
    AnchorPoint(QDeclarativeItem *parent = 0)
    : QDeclarativeItem(parent)
    , m_anchorTo(NULL)
    {
    setFlag(ItemSendsScenePositionChanges, true);
    }
    ~AnchorPoint(){}

    AnchorPoint* anchorTo() const { return m_anchorTo;}
    void setAnchorTo(AnchorPoint* anchorPoint)
    {
        if(anchorPoint != m_anchorTo)
        {
            if(m_anchorTo != NULL)
            {
                  disconnect(m_anchorTo,SIGNAL(scenePosChanged()),this,SLOT(anchorPosChanged()));
            }
            m_anchorTo = anchorPoint;
            if(m_anchorTo != NULL)
           {
                connect(m_anchorTo,SIGNAL(scenePosChanged()),this,SLOT(anchorPosChanged()));
           }
        }
    }
    
    QVariant itemChange(GraphicsItemChange change, const QVariant &value)
    {
        if (change == ItemScenePositionHasChanged && scene())
        {
            emit scenePosChanged();
        }
        return QGraphicsItem::itemChange(change, value);
    }
    

    signals:
    void anchorToChanged();
    void scenePosChanged();

    protected slots:
    void anchorPosChanged()
    {
    QGraphicsItem* anchorParent = (QGraphicsItem*)m_anchorTo->parentItem();
    if(anchorParent==NULL)
    {
    qDebug() << "Item anchored to has no parent : can't anchored to it";
    return;
    }

        if(parentItem()==NULL)
        {
            qDebug() << " Item has no parent : can't be anchored ";
            return;
        }
    
        QPointF anchorPos = m_anchorTo->pos();
        setPos(anchorParent->mapToItem((const QGraphicsItem*)parentItem(),anchorPos));
    }
    

    private:
    AnchorPoint* m_anchorTo;
    };@


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.