장소 (QML)
'장소(Places)' 예제는 장소를 검색하고 관련 콘텐츠에 접근하는 방법을 보여줍니다.

'장소' 예제는 장소를 검색하는 방법을 보여줍니다. 특히 리뷰, 이미지 및 관련 콘텐츠와 같은 추가 정보를 가져오는 방법을 보여줍니다.
예제 실행하기
다음 위치에서 예제를 실행할 수 있습니다:
- Qt Creator
Welcome 모드를 열고 Examples 에서 예제를 선택하십시오. 자세한 내용은 Qt Creator: 튜토리얼: 빌드 및 실행을 참조하십시오.
- Qt Extension for Visual Studio Code
Command Palette 에서 Qt: Open Qt examples 명령을 실행하고 목록에서 예제를 선택하십시오. 자세한 내용은 Qt Extension for Visual Studio Code: 튜토리얼: 빌드 및 실행을 참조하십시오.
이 예제는 사용 가능한 모든 지리 서비스 플러그인과 함께 작동할 수 있습니다. 그러나 일부 플러그인의 경우 올바르게 작동하기 위해 추가적인 plugin parameters 이 필요할 수 있습니다. Plugin parameters 는 --plugin 인수를 사용하여 명령줄에서 전달할 수 있으며, 그 형식은 다음과 같습니다:
--plugin.<parameter name> <parameter value>각 지리 서비스 플러그인이 지원하는 매개변수에 대한 자세한 내용은 해당 플러그인의 문서를 참조하십시오. 이 예제에서 사용하는 기본 플러그인은 Qt Location Open Street Map Plugin이며, 이 플러그인은 별도의 매개변수가 필요하지 않습니다.
개요
'장소(Places)' 예제는 지도가 표시된 애플리케이션 창을 보여줍니다. 창 상단에는 장소 검색어를 입력하는 데 사용되는 검색 상자가 있습니다. 장소를 검색하려면 텍스트 상자에 검색어를 입력하고 돋보기 아이콘을 클릭하십시오. 카테고리별로 장소를 검색하려면 카테고리 아이콘을 클릭하여 사용 가능한 카테고리 목록을 표시한 다음 원하는 카테고리를 선택하십시오. 장소 검색 쿼리는 지도에 표시된 현재 위치 근처의 장소를 대상으로 합니다.
일부 플러그인의 경우, 검색 상자에 3자 이상을 입력하면 검색어 제안이 표시됩니다. 제안된 항목 중 하나를 선택하면 선택한 검색어로 장소 검색이 수행됩니다.
검색 결과를 클릭하면 해당 장소에 대한 상세 정보가 표시됩니다. 장소에 풍부한 콘텐츠(기사, 리뷰, 이미지 등)가 있는 경우, 상세 정보 페이지의 버튼을 통해 해당 콘텐츠에 접근할 수 있습니다. 유사한 장소를 찾으려면 "유사한 장소 찾기" 버튼을 클릭하세요.
“제공업체” 메뉴를 통해 지리 정보 서비스 제공업체를 변경할 수 있습니다.
카테고리 표시
카테고리별 검색을 수행하기 전에, 사용 가능한 카테고리 목록을 가져와야 합니다. 이는 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 핸들러는 어떤 특정 카테고리가 선택되었는지를 나타내는 category 매개변수와 함께 searchCategory() 신호를 발산합니다.
CategoryDelegate는 카테고리 이름을 표시하며, Label 가 클릭되면 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();
}
}
}검색어의 길이가 3자 이상인 경우에만 제안 검색이 실행됩니다.
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 ` 객체로 설정되며, 이 객체는 ` Map`에 표시된 현재 위치와 연결된 중심점을 가진 ` geoCircle `입니다.
마지막으로, searchForCategory(), searchForText(), searchForRecommendations() 라는 세 가지 헬퍼 함수를 정의합니다. 이 함수들은 categories, searchTerm 또는 recommendationId 속성을 설정하고, 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)에는 사설, 리뷰, 이미지 등 추가적인 리치 콘텐츠가 포함될 수 있습니다. 리치 콘텐츠는 일련의 모델을 통해 액세스됩니다. 콘텐츠 모델은 일반적으로 애플리케이션 개발자가 직접 생성하지 않으며, 대신 ` Place ` 유형의 ` editorialModel`, ` reviewModel ` 및 ` imageModel ` 속성을 통해 모델을 가져옵니다.
© 2026 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.