QML如何获取Loader的位置

QML how to get the position of Loader

我想生成一个类似流的二叉树来显示与 QML 的组织关系信息。enter image description here

我的方式:

  1. 像流程示例一样创建树节点。
  2. 将树节点与 Canvas 对齐,如上图所示。我想得到 在 Loader.onLoaded.
  3. 之后点画线

我的问题:

第一步完成后。第二步,获取不到正确的树节点位置点

详情:

根据Qt文档,如果我没有明确设置Loader的宽度和高度,它会自动根据其项目大小设置。但是,我无法获取 Loader.onloaded()

中的节点位置点
import QtQuick 2.3
import QtQuick.Window 2.2

Window {
    id: appWindow

    visible: true
    width: 1066
    height: 600

    property var jsonData: [{"id":1,"pid":0},{"id":2,"pid":1},{"id":3,"pid":1},
        {"id":4,"pid":2},{"id":5,"pid":2},{"id":6,"pid":3},
        {"id":7,"pid":3},{"id":8,"pid":4},{"id":9,"pid":4},{"id":10,"pid":6}]
    property int maxRectangles: 5
    Component.onCompleted: {
        var color = Qt.lighter("red", maxRectangles / 7)
        pLoader.sourceComponent = rectangleComponent;
        var currItem = pLoader.item;
        currItem.color = "blue"
        currItem.text = 0

        var tmp = {};
        tmp[jsonData[0]["id"]] = currItem ;

        for(var index = 1; index < jsonData.length; index++) {
            color = Qt.lighter("red", index / 7)
            var sItem = jsonData[index]["pid"];
            if(tmp[sItem].rComponent.sourceComponent === null){
                tmp[sItem].rComponent.sourceComponent = rectangleComponent
                attachData(tmp[sItem].rComponent.item,index, color);
                tmp[jsonData[index]["id"]] = tmp[sItem].rComponent.item ;
            }
            else {
                tmp[sItem].lComponent.sourceComponent = rectangleComponent
                attachData(tmp[sItem].lComponent.item,index, color);
                tmp[jsonData[index]["id"]] = tmp[sItem].lComponent.item ;
            }
        }
    }
    function attachData(item, text, color){
        item.text = text;
        item.color = color
    }

    Component {
        id: rectangleComponent
        Column{
            property alias rComponent: rightLoader
            property alias lComponent: leftLoader
            property alias color: rect.color
            property alias text: myText.text
            spacing: 40
            //anchors.horizontalCenter: parent.horizontalCenter
            Rectangle {
                anchors.horizontalCenter: parent.horizontalCenter
                id:rect
                width: 100
                height: 100
                Text{
                    id:myText
                    font.pixelSize: 18
                    anchors.centerIn: parent
                    color:"white"
                }
                Component.onCompleted: {
                    console.log("Component.onCompleted", mapToItem(canvas, 0, 0))
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        updateDotPosition(parent)
                        //here, printed coordinate is my expected
                        //but i want line them after tree node loaded antomate
                        console.log("updateDotPosition", mapToItem(canvas, 0, 0))
                    }
                }
            }

            Row{
                spacing: 40
                anchors.horizontalCenter: parent.horizontalCenter
                Loader {
                    id:leftLoader
                    onLoaded: {
                        console.log("leftLoader", mapToItem(canvas, 0, 0))
                    }
                }
                Loader {
                    id:rightLoader
                    onLoaded: {
                        console.log("rightLoader", mapToItem(canvas, 0, 0))
                    }
                }
            }
        }
    }

    Canvas{
        anchors.fill: parent
        id:canvas
        Loader {
            id:pLoader
            anchors.horizontalCenter: parent.horizontalCenter
            //sourceComponent: rectangleComponent
        }
    }

    Rectangle {
        id: testMapToItemDot
        width: 20
        height: width
        radius: width / 2
        z: 1
        color: "darkblue"
    }

    function updateDotPosition(itemToMap) {
        var pos = testMapToItemDot.mapFromItem(
                    itemToMap,
                    (itemToMap.width - testMapToItemDot.width) / 2,   // these parameters are optional - I want to move the dot to
                    (itemToMap.height - testMapToItemDot.height) / 2) // the center of the object, not upper left corner
        testMapToItemDot.x += pos.x
        testMapToItemDot.y += pos.y
    }
}

加载项的位置和大小可以这样访问:Loader.item.xLoader.item.yLoader.item.widthLoader.item.height.

如果这不是您要查找的内容,请更准确地描述问题。


请查看此示例应用程序。我认为这就是您要找的东西 - 看看 function updateDotPosition(itemToMap)。请注意,如果您将 "mapping" 指向一个不移动的对象(示例中的点在移动)并且该对象将是具有相同 xCanvas 的同级对象, y 值,然后函数的结果将为您提供精确的点,从哪里画一条线。您将不需要像我一样使用 +=

import QtQuick 2.3
import QtQuick.Window 2.2

Window {
    id: appWindow

    visible: true
    width: 1066
    height: 600

    property int maxRectangles: 5

    Component {
        id: rectangleComponent
        Rectangle {
            x: 110
            y: 30
            width: 100
            height: 100
            property var loaderSource
            Component.onCompleted: {
                color = Qt.lighter("red", maxRectangles / 7)
                if (maxRectangles-- > 0)
                    loaderSource = rectangleComponent
                else
                    loaderSource = undefined
            }
            Loader {
                sourceComponent: loaderSource
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    updateDotPosition(parent)
                }
            }
        }
    }

    Loader {
        sourceComponent: rectangleComponent
    }

    Rectangle {
        id: testMapToItemDot
        width: 20
        height: width
        radius: width / 2
        z: 1
        color: "darkblue"
    }

    function updateDotPosition(itemToMap) {
        var pos = testMapToItemDot.mapFromItem(
                    itemToMap,
                    (itemToMap.width - testMapToItemDot.width) / 2,   // these parameters are optional - I want to move the dot to
                    (itemToMap.height - testMapToItemDot.height) / 2) // the center of the object, not upper left corner
        testMapToItemDot.x += pos.x
        testMapToItemDot.y += pos.y
    }
}

这是您示例的工作版本。这只是一个例子。不幸的是脏了。这是因为我有点难以理解代码中发生的事情——我相信你不会遇到这个问题。我还使用了 Timer,因为创建时 Rectangle 对象的 x,y 等于 0。我需要稍等一下。请不要在发布代码中使用它 - 这是糟糕的设计:)

import QtQuick 2.3
import QtQuick.Window 2.2

Window {
    id: appWindow

    visible: true
    width: 1066
    height: 600

    property var jsonData: [{"id":1,"pid":0},{"id":2,"pid":1},{"id":3,"pid":1},
        {"id":4,"pid":2},{"id":5,"pid":2},{"id":6,"pid":3},
        {"id":7,"pid":3},{"id":8,"pid":4},{"id":9,"pid":4},{"id":10,"pid":6}]
    property int maxRectangles: 5
    Component.onCompleted: {
        var color = Qt.lighter("red", maxRectangles / 7)
        pLoader.sourceComponent = rectangleComponent;
        var currItem = pLoader.item;
        currItem.color = "blue"
        currItem.text = 0
        connectedItems.push([currItem, null])

        var tmp = {};
        tmp[jsonData[0]["id"]] = currItem ;

        for(var index = 1; index < jsonData.length; index++) {
            color = Qt.lighter("red", index / 7)
            var sItem = jsonData[index]["pid"];
            if(tmp[sItem].rComponent.sourceComponent === null){
                tmp[sItem].rComponent.sourceComponent = rectangleComponent
                attachData(tmp[sItem].rComponent.item,index, color);
                tmp[jsonData[index]["id"]] = tmp[sItem].rComponent.item ;
                connectedItems.push([tmp[sItem].rComponent.item, null])
            }
            else {
                tmp[sItem].lComponent.sourceComponent = rectangleComponent
                attachData(tmp[sItem].lComponent.item,index, color);
                tmp[jsonData[index]["id"]] = tmp[sItem].lComponent.item ;
                connectedItems.push([tmp[sItem].lComponent.item, null])
            }
            if ( jsonData[index]["pid"] !== 0) {
                connectedItems[connectedItems.length - 1][1] = connectedItems[jsonData[index]["pid"] - 1][0]
            }
        }
    }
    function attachData(item, text, color){
        item.text = text;
        item.color = color
    }

    property var connectedItems: [];

    function paintConnection(objectFrom, objectTo) {
        var vectorStart = canvas.mapFromItem(
                    objectFrom, objectFrom.width / 2, objectFrom.height)

        var vectorEnd = canvas.mapFromItem(
                    objectTo, objectTo.width / 2,0)

        var ctx = canvas.getContext("2d");
        ctx.beginPath();
        ctx.moveTo(vectorStart.x, vectorStart.y);
        ctx.lineTo(vectorEnd.x,vectorEnd.y);
        ctx.stroke();

        canvas.requestPaint()
    }

    Timer {
        id: paintLines
        interval: 100
        running: true
        onTriggered: {
            for (var i = 1 ; i < connectedItems.length ; i++) {
                paintConnection(connectedItems[i][1].rect, connectedItems[i][0].rect)
            }
        }
    }

    Component {
        id: rectangleComponent
        Column{
            property alias rComponent: rightLoader
            property alias lComponent: leftLoader
            property alias color: rect.color
            property alias text: myText.text
            property alias rect: rect
            spacing: 40
            //anchors.horizontalCenter: parent.horizontalCenter
            Rectangle {
                anchors.horizontalCenter: parent.horizontalCenter
                id:rect
                width: 100
                height: 100
                Text{
                    id:myText
                    font.pixelSize: 18
                    anchors.centerIn: parent
                    color:"white"
                }
                Component.onCompleted: {
                    console.log("Component.onCompleted", mapToItem(canvas, 0, 0))
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        updateDotPosition(parent)
                        //here, printed coordinate is my expected
                        //but i want line them after tree node loaded antomate
                        console.log("updateDotPosition", mapToItem(canvas, 0, 0))
                    }
                }
            }

            Row{
                spacing: 40
                anchors.horizontalCenter: parent.horizontalCenter
                Loader {
                    id:leftLoader
                    onLoaded: {
                        console.log("leftLoader", mapToItem(canvas, 0, 0))
                    }
                }
                Loader {
                    id:rightLoader
                    onLoaded: {
                        console.log("rightLoader", mapToItem(canvas, 0, 0))
                    }
                }
            }
        }
    }

    Canvas{
        anchors.fill: parent
        id:canvas
        Loader {
            id:pLoader
            anchors.horizontalCenter: parent.horizontalCenter
            //sourceComponent: rectangleComponent
        }
    }

    Rectangle {
        id: testMapToItemDot
        width: 20
        height: width
        radius: width / 2
        z: 1
        color: "darkblue"
    }

    function updateDotPosition(itemToMap) {
        var pos = testMapToItemDot.mapFromItem(
                    itemToMap,
                    (itemToMap.width - testMapToItemDot.width) / 2,   // these parameters are optional - I want to move the dot to
                    (itemToMap.height - testMapToItemDot.height) / 2) // the center of the object, not upper left corner
        testMapToItemDot.x += pos.x
        testMapToItemDot.y += pos.y
    }
}