对 QML 组件的无效别名引用

Invalid alias reference to a QML component

我正在使用 QML 和 Qt 5.11 开发开放式街道地图功能。 我刚刚写了一个最小的复制案例。它包括在地图上显示一个蓝点。当我用鼠标滚轮改变地图缩放时,会计算比例因子,当这个比例大于0.1时,应该显示蓝点,否则不显示。

在我的地图组件中,我创建了一个比例尺 属性,它是 scaleFactor() 函数的别名。 在我的地图中,我使用 MapItemView which contains a MapQuickItem as a delegate, and this MapQuickItem contains a Rectangle which contains a Canvas.

我的第一个想法是在我的 Rectangle 中添加一个 属性 别名以获得 onScaleChanged 事件,如下所示:

property alias scale: map.scale
onScaleChanged: {
    myCanvas.requestPaint();
}

但这不起作用,因为 qmlscene 工具中的错误消息是:

Line 51 Invalid alias reference. Unable to find id "map".

我很确定问题出在 MapItemView 中,但我找不到任何相关信息。

非常感谢任何帮助。

myfile.qml

上的内容
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtLocation 5.11
import QtPositioning 5.11
import QtQuick.Window 2.11

Window {
    id: root; width: 800; height: 600;

    Plugin { id: mapPlugin; name: "osm"; }

    ListModel {
        id: myModel
        ListElement { latitude: 48.2351164; longitude: 6.8986936; }
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(48.2351164, 6.8986936)
        zoomLevel: 19

        property real scale: map.scaleFactor()
        onScaleChanged: {
            console.log(">>> scale from map component", scale);
        }

        // function reimplemented from qdeclarativegeomapquickitem.cpp
        function scaleFactor() {
            return Math.pow(0.5, map.maximumZoomLevel - map.zoomLevel);
        }

        MapItemView {
            model: myModel

            delegate: MapQuickItem {
                anchorPoint.x: myRect.width / 2
                anchorPoint.y: myRect.height / 2
                width: myRect.width
                height: myRect.height

                zoomLevel: map.maximumZoomLevel // set the zoomLevel (1:1 scale)
                coordinate: QtPositioning.coordinate(model.latitude, model.longitude)

                sourceItem: Rectangle {
                    id: myRect

                    readonly property int radius: 50

                    property alias scale: map.scale
                    onScaleChanged: {
                        myCanvas.requestPaint();
                    }

                    width: radius * 2
                    height: radius * 2
                    color: "transparent"

                    Canvas {
                        id: myCanvas
                        anchors.fill: parent

                        onPaint: {
                            var width = myRect.width;
                            var height = myRect.height;
                            var centreX = width / 2;
                            var centreY = height / 2;
                            var ctx = getContext("2d");
                            ctx.reset();

                            if (scale > 0.1) {
                                ctx.fillStyle = "blue";
                                ctx.globalAlpha = 1;
                                ctx.beginPath();
                                ctx.moveTo(centreX, centreY);
                                ctx.arc(centreX, centreY, myRect.radius, 0, Math.PI * 2, false);
                                ctx.closePath();
                                ctx.fill();
                            }
                        }
                    }
                }
            }
        }
    }
}

只需 运行 qmlscene myfile.qml 即可执行。

下面的工作代码。

问题在于,虽然 MapQuickItem 似乎位于 myfile.qml 组件的命名空间中,但实际上并非如此。委托实际上是组件本身,并启动一个新的命名空间。您可以认为委托在一个新的匿名组件中结束,该组件无法引用它出现在的周围组件中的 id。

但是,虽然它不能引用周围组件中的 id,但它可以引用组件视觉层次结构中的组件 id。因此,您 可以 在 运行 时构建此视觉层次结构时实例化的组件层次结构中引用根(此文件组件本身的 ID)。

从那里,您可以在根对象上将此组件私有的地图组件作为别名公布,然后通过 MapItemView 中的根引用来引用它。

起初有点令人困惑,但当您开始意识到组件是由文件、组件或委托定义的 QML 集群时,它就有意义了:定义。组件内 id 引用很好,但外部引用必须作为属性向下传递,或者在委托情况下通过组件层次结构向上引用。然后必须将组件的子 QML 对象公布为别名以提供外部访问。

更多信息在这里:

https://doc.qt.io/qt-5/qtqml-documents-scope.html

import QtQuick 2.11
import QtQuick.Controls 2.4
import QtLocation 5.11
import QtPositioning 5.11
import QtQuick.Window 2.11

Window {
    id: root; width: 800; height: 600;

    visible: true

    property alias map: map

    Plugin { id: mapPlugin; name: "osm"; }

    ListModel {
        id: myModel
        ListElement { latitude: 48.2351164; longitude: 6.8986936; }
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(48.2351164, 6.8986936)
        zoomLevel: 19

        property real scale: map.scaleFactor()
        onScaleChanged: {
            console.log(">>> scale from map component", scale);
        }

        // function reimplemented from qdeclarativegeomapquickitem.cpp
        function scaleFactor() {
            return Math.pow(0.5, map.maximumZoomLevel - map.zoomLevel);
        }

        MapItemView {
            model: myModel

            delegate: MapQuickItem {
                anchorPoint.x: myRect.width / 2
                anchorPoint.y: myRect.height / 2
                width: myRect.width
                height: myRect.height

                zoomLevel: map.maximumZoomLevel // set the zoomLevel (1:1 scale)
                coordinate: QtPositioning.coordinate(model.latitude, model.longitude)

                sourceItem: Rectangle {
                    id: myRect

                    readonly property int radius: 50

                    property real scale: root.map.scale
                    onScaleChanged: {
                        myCanvas.requestPaint();
                    }

                    width: radius * 2
                    height: radius * 2
                    color: "transparent"

                    Canvas {
                        id: myCanvas
                        anchors.fill: parent

                        onPaint: {
                            var width = myRect.width;
                            var height = myRect.height;
                            var centreX = width / 2;
                            var centreY = height / 2;
                            var ctx = getContext("2d");
                            ctx.reset();

                            if (scale > 0.1) {
                                ctx.fillStyle = "blue";
                                ctx.globalAlpha = 1;
                                ctx.beginPath();
                                ctx.moveTo(centreX, centreY);
                                ctx.arc(centreX, centreY, myRect.radius, 0, Math.PI * 2, false);
                                ctx.closePath();
                                ctx.fill();
                            }
                        }
                    }
                }
            }
        }
    }
}