地点 (QML)

地点示例演示了如何搜索地点并访问相关内容。

地点示例演示了如何搜索地点。它特别展示了如何检索评论、图片和相关内容等更多信息。

运行示例

要运行来自 Qt Creator,打开Welcome 模式,然后从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建和运行

该示例可与任何可用的地理服务插件配合使用。不过,某些插件可能需要额外的plugin parameters 才能正常运行。Plugin parameters 可以使用--plugin 参数在命令行中传递,其形式为

--plugin.<parameter name> <parameter value>

有关每个地理服务插件支持哪些插件参数的详细信息,请参阅每个插件的文档。本示例使用的默认插件是Qt Location Open Street Map Plugin,它不需要任何参数。

概述

Places 示例展示了一个显示地图的应用程序窗口。窗口顶部有一个搜索框,用于输入地点搜索查询。要搜索地点,请在文本框中输入搜索词,然后点击放大镜图标。要按类别搜索地点,请单击类别图标以显示可用类别列表,然后选择所需的类别。地点搜索查询将搜索地图上显示的当前位置附近的地点。

对于某些插件,当输入三个或三个以上字符时,搜索框会提供搜索词建议。选择其中一个建议将导致使用所选搜索文本执行地点搜索。

点击搜索结果将显示该地点的详细信息。如果一个地方有丰富的内容(社论、评论和图片),可以通过详细信息页面上的按钮访问这些内容。要查找类似地点,请点击 "查找类似 "按钮。

地理服务提供商可以通过访问 "提供商 "菜单进行更改。

显示类别

在按类别进行搜索之前,需要检索可用类别列表。这可以通过创建CategoryModel 来实现。

CategoryModel {
    id: categoryModel
    hierarchical: true
}

CategoryModel 类型提供了可用类别的模型。它可以提供平面列表或分层树模型。在本示例中,我们将hierarchical 属性设置为true,从而使用层次树模型。plugin 属性在示例初始化时设置。

接下来,我们创建一个ListView 来显示类别模型。

ListView {
    id: root
    property var categoryModel
    property var rootIndex

    signal searchCategory(var category)
    signal showSubcategories(var index)

    snapMode: ListView.SnapToItem

    model: DelegateModel {
        id: delegeteDataModel
        model: root.categoryModel
        rootIndex: root.rootIndex
        delegate: CategoryDelegate {
            width: ListView.view.width
            onSearchCategory: root.searchCategory(category);
            onShowSubcategories: root.showSubcategories(delegeteDataModel.modelIndex(index));
        }
    }
}

由于使用的是分层模型,因此需要一个DelegateModel 来提供导航功能。如果使用的是平面列表模型,则视图可以直接使用CategoryModel

rootIndex属性设置了DelegateModel 的根索引。类别由CategoryDelegate 显示,它提供了两个信号。onShowSubcategories会发出showSubcategories()信号,根索引为当前索引,从而显示所选类别的子类别。onSearchCategory处理程序会发出带有类别参数的searchCategory()信号,表明选择了哪个特定类别。

当点击Label 时,CategoryDelegate 会显示类别名称并发出searchCategory()信号:

Label {
    id: labelItem
    text: category.name
    anchors.left: icon.right
    anchors.right: parent.right
    anchors.verticalCenter: parent.verticalCenter
}

TapHandler {
    id: tapHanlder
    onTapped: {
        if (model.hasModelChildren) {
            root.showSubcategories()
        } else {
            root.searchCategory()
        }
    }
}

显示搜索建议

PlaceSearchSuggestionModel 类型用于根据部分输入的搜索词获取建议搜索词。

只要输入的搜索词发生变化,就会触发新的建议搜索。

SearchBar {
    id: searchBar
    onSearchTextChanged: function (searchText) {
        if (searchText.length >= 3 && suggestionModel != null) {
            suggestionModel.searchTerm = searchText;
            suggestionModel.update();
        }
    }
}

只有当搜索词长度为三个或三个以上字符时,才会查询建议。

PlaceSearchSuggestionModel 的状态发生变化时,就会显示搜索建议。

PlaceSearchSuggestionModel {
    id: suggestionModel
    searchArea: searchRegion

    onStatusChanged: {
        if (status == PlaceSearchSuggestionModel.Ready)
            stackView.showSuggestions()
    }
}

SuggestionsShown "状态下的主要对象是显示搜索建议的ListView

ListView {
    id: suggestionView
    property var suggestionModel
    signal suggestionSelected(string text)

    model: suggestionModel
    delegate: Item {
        width: ListView.view.width
        height: label.height * 1.5
        Label {
            id: label
            text: suggestion
        }
        MouseArea {
            anchors.fill: parent
            onClicked: suggestionSelected(suggestion)
        }
    }
}

Label 对象被用作显示建议文本的委托。点击建议搜索词会更新搜索词,并使用搜索建议触发位置搜索。

搜索地点

PlaceSearchModel 类型用于搜索地点。

PlaceSearchModel {
    id: placeSearchModel
    searchArea: searchRegion

    function searchForCategory(category) {
        searchTerm = "";
        categories = category;
        recommendationId = "";
        searchArea = searchRegion
        limit = -1;
        update();
    }

    function searchForText(text) {
        searchTerm = text;
        categories = null;
        recommendationId = "";
        searchArea = searchRegion
        limit = -1;
        update();
    }

    function searchForRecommendations(placeId) {
        searchTerm = "";
        categories = null;
        recommendationId = placeId;
        searchArea = null;
        limit = -1;
        update();
    }

    onStatusChanged: {
        switch (status) {
        case PlaceSearchModel.Ready:
            if (count > 0)
                stackView.showPlaces()
            else
                stackView.showMessage(qsTr("Search Place Error"),qsTr("Place not found !"))
            break;
        case PlaceSearchModel.Error:
            stackView.showMessage(qsTr("Search Place Error"),errorString())
            break;
        }
    }
}

首先要设置模型的一些属性,这些属性将用于形成搜索请求。searchArea 属性被设置为searchRegion对象,该对象是一个geocircle ,其中心与Map 上显示的当前位置相关联。

最后,我们定义了三个辅助函数searchForCategory()searchForText()searchForRecommendations(),分别设置categoriessearchTermrecommendationId 属性,并调用update() 方法开始地点搜索。搜索结果显示在ListView 中。

ListView {
    id: searchView

    property var placeSearchModel
    signal showPlaceDetails(var place, var distance)
    signal showMap()

    model: placeSearchModel
    delegate: SearchResultDelegate {
        width: ListView.view.width
        onShowPlaceDetails: function (place, distance) { searchView.showPlaceDetails(place, distance) }
        onSearchFor: function (query) { placeSearchModel.searchForText(query) }
    }

    footer: RowLayout {
        width: parent.width

        Button {
            text: qsTr("Previous")
            enabled: placeSearchModel.previousPagesAvailable
            onClicked: placeSearchModel.previousPage()
            Layout.alignment: Qt.AlignHCenter
        }

        Button {
            text: qsTr("Clear")
            onClicked: {
                placeSearchModel.reset()
                showMap()
            }
            Layout.alignment: Qt.AlignHCenter
        }

        Button {
            text: qsTr("Next")
            enabled: placeSearchModel.nextPagesAvailable
            onClicked: placeSearchModel.nextPage()
            Layout.alignment: Qt.AlignHCenter
        }
    }
}

ListView 中使用的委托,即SearchResultDelegate,旨在通过Loader 对象处理多种搜索结果类型。对于PlaceResult类型的结果,该委托是:

Component {
    id: placeComponent
    Item {
        id: placeRoot
        width: root.width
        height: Math.max(icon.height, 3 * placeName.height)

        Rectangle {
            anchors.fill: parent
            color: "#44ffffff"
            visible: mouse.pressed
        }

        Rectangle {
            anchors.fill: parent
            color: "#dbffde"
            visible: model.sponsored !== undefined ? model.sponsored : false

            Label {
                text: qsTr("Sponsored result")
                horizontalAlignment: Text.AlignRight
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                font.pixelSize: 8
                visible: model.sponsored !== undefined ? model.sponsored : false
            }
        }

        GridLayout {
            rows: 2
            columns: 2
            anchors.fill: parent
            anchors.leftMargin: 30
            flow: GridLayout.TopToBottom

            Image {
                // anchors.verticalCenter: parent.verticalCenter
                id:icon
                source: place.favorite ? Qt.resolvedUrl("../resources/star.png") : place.icon.url()
                Layout.rowSpan: 2
            }

            Label {
                id: placeName
                text: place.favorite ? place.favorite.name : place.name
                Layout.fillWidth: true
            }

            Label {
                id: distanceText
                font.italic: true
                text: Helper.formatDistance(distance)
                Layout.fillWidth: true
            }
        }

        Rectangle {
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.margins: 15
            height: 1
            color: "#46a2da"
        }

        MouseArea {
            id: mouse
            anchors.fill: parent
            onClicked: {
                if (model.type === undefined || type === PlaceSearchModel.PlaceResult) {
                    if (!place.detailsFetched)
                        place.getDetails();
                    root.showPlaceDetails(model.place, model.distance);
                }
            }
        }
    }
}

显示地点内容

地点可以有额外的丰富内容,包括社论、评论和图片。丰富的内容可通过一组模型访问。应用程序开发人员一般不会直接创建内容模型,而是从Place 类型的editorialModelreviewModelimageModel 属性中获取模型。

ListView {
    id:view
    property Place place
    signal showEditorial(var editorial)
    model: place.editorialModel
    delegate: EditorialDelegate {
        width: ListView.view.width
        onShowEditorial: view.showEditorial(model)
    }
}

示例项目 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.