[Solved] Item::mapToItem(): wrong returned values



  • UPDATE 2: Solved. I was using the function in a wrong way!
    Solution: http://qt-project.org/forums/viewreply/114331/


    UPDATE: Read next post for a minimal example code.


    Hi all!
    I'd like to write my very first message in this forum since the acquisition by Digia and creation of the Qt Project.

    My problem is that Item::mapToItem(null, x, y) is giving wrong values. As I'm a beginner about the new Qt Quick 2, and QML in general, it is most probably due to a misuse but I cannot find it.

    A maybe-not-so-minimal working example to show my case follows. It creates a couple of rectangles with a Repeater, and fails at finding the central point for the second one (stored in posX, posY properties). Being squares of size 25, I'd expect the second one to stay at (0, 25), with its center at (0, 37.5), but mapToItem() says that it is placed at (0, 50), so the center calculations go for (0, 67.5) instead.

    I would bet the culprit is the reparenting done to the items, but that's the only way I found to have a clean list of Rectangles: if the Repeater was placed inside the Column, as most Qt examples show, then the Column's children would include that Repeater, which is not nice for code abstraction (other parts of the code shouldn't have to deal with that detail).

    @
    import QtQuick 2.0

    Item {
    id: root
    width: buttonSize
    height: (2 * buttonCount + 1) * buttonSize

    property int buttonCount: 2
    property real buttonSize: 25
    property alias buttons: container.children
    
    Component.onCompleted: {
        console.log("root: onCompleted")
        for (var i = 0; i < buttons.length; ++i) {
            console.log("root:", buttons[i].buttonId, "x", buttons[i].x, "y", buttons[i].y)
            buttons[i].posUpdate()
        }
    }
    
    Column {
        id: container
        anchors {
            fill: parent
    

    // topMargin: buttonSize
    }
    // spacing: buttonSize
    }

    Repeater {
        model: buttonCount
    
        Item {
            // Use a dummy Item to wrap our Socket element.
            // The wrapper will become child of the Repeater's parent,
            // while the Socket will be reparented to our container.
            id: wrapper
    
            Rectangle {
                id: button
                width: buttonSize; height: buttonSize; radius: buttonSize
                color: "red"
    
                property string buttonId: "button_" + index
                property real posX
                property real posY
    
                function posUpdate() {
                    console.log("button: posUpdate", buttonId, "button.x", x, "button.y", y, "mapToItem().x", mapToItem(null, x, y).x, "mapToItem().y", mapToItem(null, x, y).y)
                    posX = button.mapToItem(null, x, y).x + (buttonSize / 2.0)
                    posY = button.mapToItem(null, x, y).y + (buttonSize / 2.0)
                    console.log("button: posUpdate", buttonId, "posX", posX, "posY", posY)
                }
    
                Component.onCompleted: {
                    parent = container
                }
            }
        }
    }
    

    }
    @

    Any help would be appreciated :-)



  • OK I have a new, minimal example to show this problem:

    @
    import QtQuick 2.0

    Item {
    id: window
    width: 25; height: 50

    Rectangle {
        id: button_1
        x: 0; y: 0
        width: 25; height: 25
        color: "red"
    
        Component.onCompleted: {
            var obj = mapToItem(null, x, y)
            console.log("[button_1]", "x:", x, "y:", y, "mapToItem().x:", obj.x, "mapToItem().y", obj.y)
        }
    }
    
    Rectangle {
        id: button_2
        x: 0; y: 25
        width: 25; height: 25
        color: "pink"
    
        Component.onCompleted: {
            var obj = mapToItem(null, x, y)
            console.log("[button_2]", "x:", x, "y:", y, "mapToItem().x:", obj.x, "mapToItem().y", obj.y)
        }
    }
    

    }
    @

    Expected output:
    @[button_2] x: 0 y: 25 mapToItem().x: 0 mapToItem().y 25@

    Obtained output:
    @[button_2] x: 0 y: 25 mapToItem().x: 0 mapToItem().y 50@



  • Responding to a weird issue, that may even be the core problem;
    @
    "if the Repeater was placed inside the Column [] then the Column’s children would include that Repeater
    which is not nice for code abstraction (other parts of the code shouldn’t have to deal with
    that detail)."
    @

    This looks weird, the idea behind QML and scenegraphs in general is that this kind of details are irrelevant to the rest of the codebase.
    Just because there is a repeater in between should have zero effect on other code.

    You use the name (id: foo) of an element.

    Maybe you are trying to fetch an item from a C++ code using the QObject:child() method, I would strongly suggest you do not do that ;)

    Maybe you can explain exactly what the problem is that you ran into that made you reparent? Its likely that its the one that should be solved first.



  • Note that in my second post I included a minimal example which shows the same wrong results on Item::mapToItem(), without any reparenting.

    Replying to your question:

    My project is pure QML + JavaScript so far; I just want to process a list of items all in the same way, which means all must be of the same type.

    I'm using a Canvas item which paints lines; these lines are parametrized by properties of some items, previously instantiated inside a Column. Ideally, the JS code on the Canvas::onPaint() method would use a "for" loop over all of the Colum's children, but that approach doesn't work because one of the children is a Repeater, thus not being of the expected type and not having the expected properties.

    Maybe I'm not doing this the "QML way"? But I didn't find a better way...



  • Passing in null as the first parameter of mapToItem is currently broken. There are some changes in codereview to address this issue, but currently the script engine returned is null. In the future, this function should map it from top left of the top-level application window; currently the behaviour is undefined.

    Cheers,
    Chris.



  • I assumed that there was no need for a bug report because this issue is already a work in progress, but after searching in the Qt bugreports site for "mapToItem" and "mapFromItem", I cannot find any proper report about this issue.

    So I'm adding one for being able to track this bug.

    Thank you all for the ideas and chrisadams for the explanation



  • Hi,

    parameters x,y are not required in maptoItem. Just put it as 0 for both x and y and then try.

    Regards
    ansif



  • [quote author="ansifpi" date="1360752153"]Hi,

    parameters x,y are not required in maptoItem. Just put it as 0 for both x and y and then try.

    Regards
    ansif [/quote]

    Thank you very much!!
    Thanks to this comment I realized that the mapToItem() function is working properly, it's just that I was misusing it in my examples!! The cause of the problem is a conceptual error.

    Note that in both examples I was using
    @mapToItem(null, x, y)@
    which would map to the Window the point (x, y) inside the rectangle. In my second example, the point (0, 25) inside of the second rectangle would effectively correspond to the point (0, 50) in the Window.

    Correct way of using the function in my sample would be:
    @mapToItem(null, 0, 0)@

    And for my specific need of mapping the center point:
    @mapToItem(null, width / 2.0, height / 2.0)@

    Thanks everybody for the help.



  • Good luck!!!!!!!!!


Log in to reply
 

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