简体   繁体   中英

In QML, (how) can I make MapItemGroup as a MapItemView's delegate component?

Situation: I am able to use QML Map item with Model/View/delegate. I am able to work with individual items.

Problem: As a next step, I would like to be able to draw multiple items. I need to put multiple QML MapItems (like MapCircle , MapRectangle , etc..) in a single delegate component. In general, QML supports multiple items within a delegate. The problem is with MapItemView 's delegate: it does not support multiple children items.

My Approach:

  1. I thought using MapItemGroup would work. But it seems like I am missing something. The documentation is also not so extensive on how I can make it work as a delegate component. The attached snippet shows this implementation.

    Qt's Documentation on MapItemGroup

In the code below:

  • delegateCircle, delegateRect work fine
  • delegateGroup is not displayed

A simple implementation:

import QtQuick 2.10
import QtPositioning 5.6
import QtLocation 5.9
import QtQuick.Controls 2.3 as QQc2

QQc2.ApplicationWindow {
    visible: true
    width: 640
    height: 480
    // Some list model
    ListModel {
        id: someModel
        ListElement {lat: 0; lon: 0}
        ListElement {lat: 5; lon: 0}
        ListElement {lat: 5; lon: 5}
        ListElement {lat: 0; lon: 5}
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: Plugin {name: "osm"}
        center: QtPositioning.coordinate(2.5, 2.5)
        zoomLevel: 6

        // Some views to test the model
        // delegateCircle, delegateRect work fine
        // delegateGroup is not displayed 
        MapItemView {
            model: someModel
            delegate: MapCircle {
                id: delegateCircle
                border.color: "red"
                border.width: 1
                center: QtPositioning.coordinate(model.lat, model.lon)
                radius: 50*1000
            }
        }

        MapItemView {
            model: someModel
            delegate: MapRectangle {
                id: delegateRect
                border.color: "green"
                border.width: 3
                topLeft     : QtPositioning.coordinate(model.lat+1, model.lon-1)
                bottomRight : QtPositioning.coordinate(model.lat-1, model.lon+1)
            }
        }

        MapItemView {
            model: someModel
            delegate: MapItemGroup {
                id: delegateGroup
                MapCircle {
                    id: innerCircle
                    border.color: "green"
                    border.width: 3
                    center: QtPositioning.coordinate(model.lat, model.lon)
                    radius: 75*1000
                }

                MapRectangle {
                    id: innerRect
                    border.color: "red"
                    border.width: 6
                    topLeft     : QtPositioning.coordinate(model.lat+2, model.lon-2)
                    bottomRight : QtPositioning.coordinate(model.lat-2, model.lon+2)
                }
            }
        }
    }
}

The output of the above code is: QML 代码的输出

  1. I also tried using the MapItemGroup as a sourceItem of MapQuickItem type. This did not work either.

What I want to achieve:

Well. I need to draw multiple map items using a MapItemView . Any other solution/method (including c++ backend program) is welcome.

EDIT

Thank you @GrecKo and @Yoann. Both of your solutions work. However, I chose to continue with Instantiator since it is more suited for my application.

I also found this interesting after seeing your solution: a developer's discussion on populating a model using Repeater and Instantiator .

Unfortunately, MapItemView only works with MapItem -derived items, and MapItemGroup is not one of them.

You could use a Repeater and it will work for delegates created from the start, but it won't work if you add rows in your model later on. I explained in this other answer why Repeater is not well suited for a Map .

In my answer I advise to use MapItemView but it's not applicable here. Hopefully, there is still one last available solution :
Instantiator with Map.addMapItemGroup() and Map.removeMapItemGroup() .

import QtQuick 2.10
import QtPositioning 5.6
import QtLocation 5.9
import QtQuick.Controls 2.3 as QQc2
import QtQml 2.2

QQc2.ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ListModel {
        id: someModel
        ListElement {lat: 0; lon: 0}
        ListElement {lat: 5; lon: 0}
        ListElement {lat: 5; lon: 5}
        ListElement {lat: 0; lon: 5}
    }

    Timer {
        interval: 1000
        running: true
        repeat: true
        property bool toggle: true
        onTriggered: {
            if (toggle)
                someModel.append({lat: 2.5, lon: 2.5});
            else
                someModel.remove(4);
            toggle = !toggle;
        }
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: Plugin {name: "osm"}
        center: QtPositioning.coordinate(2.5, 2.5)
        zoomLevel: 6

        Instantiator {
            model: someModel
            delegate: MapItemGroup {
                id: delegateGroup
                MapCircle {
                    id: innerCircle
                    border.color: "green"
                    border.width: 3
                    center: QtPositioning.coordinate(model.lat, model.lon)
                    radius: 75*1000
                }

                MapRectangle {
                    id: innerRect
                    border.color: "red"
                    border.width: 6
                    topLeft     : QtPositioning.coordinate(model.lat+2, model.lon-2)
                    bottomRight : QtPositioning.coordinate(model.lat-2, model.lon+2)
                }
            }
            onObjectAdded: map.addMapItemGroup(object)
            onObjectRemoved: map.removeMapItemGroup(object)
        }
    }
}

You could use a simple Repeater instead of MapItemView

import QtQuick 2.10
import QtPositioning 5.6
import QtLocation 5.9
import QtQuick.Controls 2.3

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    // Some list model
    ListModel {
        id: someModel
        ListElement {lat: 0; lon: 0}
        ListElement {lat: 5; lon: 0}
        ListElement {lat: 5; lon: 5}
        ListElement {lat: 0; lon: 5}
    }

    Map {
        id: map
        anchors.fill: parent
        plugin: Plugin {name: "osm"}
        center: QtPositioning.coordinate(2.5, 2.5)
        zoomLevel: 6

        Repeater
        {
            model: someModel
            MapItemGroup {
                id: delegateGroup
                MapCircle {
                    id: innerCircle
                    border.color: "green"
                    border.width: 3
                    center: QtPositioning.coordinate(model.lat, model.lon)
                    radius: 75*1000
                }

                MapRectangle {
                    id: innerRect
                    border.color: "red"
                    border.width: 6
                    topLeft     : QtPositioning.coordinate(model.lat+2, model.lon-2)
                    bottomRight : QtPositioning.coordinate(model.lat-2, model.lon+2)
                }

                Component.onCompleted: map.addMapItemGroup(this)
            }
        }
    }
}

As GrecKo pointed out, in order for this to work with dynamic model, the itemGroup must be "manually" added to the map, hence the line Component.onCompleted: map.addMapItemGroup(this)

That was fixed in Qt 5.12 , so You may use Your code as is now with

import QtLocation 5.12
import QtPositioning 5.12

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM