Plane Spotter (QML)
Plane Spotter 예제는 위치 및 위치 결정 데이터 유형이 QML에 긴밀하게 통합된 방식을 보여줍니다.

Plane Spotter 예제는 위치 및 위치 정보 관련 C++ 데이터 유형을 QML에 통합하고, 그 반대의 경우도 어떻게 통합하는지 보여줍니다. 이는 CPU 집약적인 위치 계산을 네이티브 환경에서 실행하되, 그 결과를 QML을 사용하여 표시해야 할 때 유용합니다.
이 예제는 유럽 지도와 유럽을 가로지르는 두 노선을 비행하는 비행기들을 보여줍니다. 첫 번째 비행기는 오슬로와 베를린 사이를, 두 번째 비행기는 런던과 베를린 사이를 왕복합니다. 각 비행기의 위치 추적은 C++로 구현되었습니다. 오슬로-베를린 노선의 비행기는 QML로 조종되며, 런던-베를린 노선의 비행기는 C++ 조종사가 제어합니다.
예제 실행
다음 경로에서 예제를 실행할 수 있습니다:
- Qt Creator
Welcome 모드를 열고 Examples 에서 예제를 선택하십시오. 자세한 내용은 Qt Creator: 튜토리얼: 빌드 및 실행을 참조하십시오.
- Qt Extension for Visual Studio Code
Command Palette 에서 Qt: Open Qt examples 명령을 실행하고 목록에서 예제를 선택합니다. 자세한 내용은 Qt Extension for Visual Studio Code: 튜토리얼: 빌드 및 실행을 참조하십시오.
개요
이 예제는 위치 컨트롤러 구현의 일환으로 ` Q_GADGET ` 기능을 사용합니다. 이를 통해 `QObject ` 기반이 아닌 C++ 값 유형을 QML에 직접 통합할 수 있습니다.
PlaneController 클래스의 주된 목적은 주어진 시점의 비행기 현재 좌표를 추적하는 것입니다. 이 클래스는 position 속성을 통해 위치를 노출합니다.
class PlaneController: public QObject
{
Q_OBJECT
Q_PROPERTY(QGeoCoordinate position READ position WRITE setPosition NOTIFY positionChanged)
// ...
};이 예제의 ` main() ` 함수는 ` PlaneController ` 클래스 인스턴스를 QML 컨텍스트에 바인딩하는 역할을 합니다:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
PlaneController oslo2berlin;
PlaneController berlin2london;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("oslo2Berlin", &oslo2berlin);
engine.rootContext()->setContextProperty("berlin2London", &berlin2london);
engine.load(QUrl(QStringLiteral("qrc:/planespotter.qml")));
return app.exec();
}QObject 에서 파생된 클래스와 마찬가지로, QGeoCoordinate 도 별도의 QML 래퍼 없이 통합할 수 있습니다.
비행기 조종하기
앞서 언급했듯이, PlaneController 클래스의 주된 목적은 두 항공편(오슬로-베를린 및 런던-베를린)의 현재 위치를 추적하고 이를 속성으로 QML 레이어에 제공하는 것입니다. 부차적인 목적은 주어진 비행 경로를 따라 항공편의 경로를 설정하고 진행 상황을 관리하는 것입니다. 어떤 의미에서는 조종사 역할을 할 수 있습니다. 이는 한 지리적 좌표에서 다른 좌표로의 이동을 애니메이션으로 표현할 수 있는 CoordinateAnimation 와 매우 유사합니다. 이 예제는 PlaneController 의 위치 속성이 PlaneController 자체의 조종 기능을 사용하는 C++ 코드에 의해 수정되는 방식과, CoordinateAnimation 를 조종사로 사용하는 QML 코드에 의해 수정되는 방식을 보여줍니다. 오슬로-베를린 노선의 비행기는 QML 코드를 사용하여 애니메이션이 적용되고, 런던-베를린 노선의 비행기는 C++ 코드를 사용하여 애니메이션이 적용됩니다.
어떤 파일럿을 사용하든, 파일럿의 동작에 따른 결과는 C++과 QML 모두에서 확인할 수 있으므로, 이 예제는 C++/QML 경계를 넘어 위치 데이터가 방해받지 않고 직접 교환되는 방식을 보여줍니다.
각 Plane 의 시각적 표현은 MapQuickItem 유형을 사용하여 이루어지며, 이를 통해 임의의 QtQuick 항목을 맵에 삽입할 수 있습니다:
// Plane.qml
MapQuickItem {
id: plane
property string pilotName;
property int bearing: 0;
anchorPoint.x: image.width/2
anchorPoint.y: image.height/2
sourceItem: Item {
//...
}
}C++ 파일럿
C++ 비행기는 C++로 조종됩니다. 컨트롤러 클래스의 ` from ` 및 ` to ` 속성은 출발지와 목적지를 설정하며, 조종사는 이를 사용하여 비행기의 방위각을 계산합니다:
Q_PROPERTY(QGeoCoordinate from READ from WRITE setFrom NOTIFY fromChanged)
Q_PROPERTY(QGeoCoordinate to READ to WRITE setTo NOTIFY toChanged)조종사는 QBasicTimer 및 QTimerEvents 를 사용하여 위치를 지속적으로 업데이트합니다. 각 타이머 반복 주기마다 PlaneController::updatePosition() 가 호출되어 새로운 위치가 계산됩니다.
void updatePosition()
{
// simple progress animation
qreal progress;
QTime current = QTime::currentTime();
if (current >= finishTime) {
progress = 1.0;
timer.stop();
} else {
progress = ((qreal)startTime.msecsTo(current) / ANIMATION_DURATION);
}
setPosition(coordinateInterpolation(
fromCoordinate, toCoordinate, easingCurve.valueForProgress(progress)));
if (!timer.isActive())
emit arrived();
}새로운 위치가 계산되면 setPosition() 가 호출되며, 이 속성의 후속 변경 알림을 통해 새로운 위치가 QML 레이어로 전달됩니다.
C++ 평면은 평면을 클릭하여 시작됩니다:
Plane {
id: cppPlane
pilotName: "C++"
coordinate: berlin2London.position
TapHandler {
onTapped: {
if (cppPlaneAnimation.running || berlin2London.isFlying()) {
console.log("Plane still in the air.");
return;
}
berlin2London.swapDestinations();
cppPlaneAnimation.rotationDirection = berlin2London.position.azimuthTo(berlin2London.to)
cppPlaneAnimation.start();
cppPlane.departed();
}
}
}azimuthTo()는 한 좌표에서 다른 좌표까지의 방위각을 도 단위로 계산합니다. 위의 코드는 QML 애니메이션을 활용하여 회전과 위치 변경을 하나의 애니메이션 흐름으로 통합하고 있다는 점에 유의하십시오:
SequentialAnimation {
id: cppPlaneAnimation
property real rotationDirection : 0;
NumberAnimation {
target: cppPlane; property: "bearing"; duration: 1000
easing.type: Easing.InOutQuad
to: cppPlaneAnimation.rotationDirection
}
ScriptAction { script: berlin2London.startFlight() }
}먼저, ` NumberAnimation `가 비행기를 올바른 방향으로 회전시키고, 이 작업이 완료되면 ` startFlight() ` 함수가 비행기의 위치 변경을 시작합니다.
public slots:
void startFlight()
{
if (timer.isActive())
return;
startTime = QTime::currentTime();
finishTime = startTime.addMSecs(ANIMATION_DURATION);
timer.start(15, this);
emit departed();
}QML 파일럿
CoordinateAnimation 유형은 오슬로에서 베를린으로, 또는 그 반대로의 비행을 제어하는 데 사용됩니다. 이는 위의 ScriptAction 을 대체합니다.
CoordinateAnimation {
id: coordinateAnimation; duration: 5000
target: oslo2Berlin; property: "position"
easing.type: Easing.InOutQuad
}QML 비행기의 ` TapHandler `는 항로 설정 로직을 구현하며, 필요할 때 애니메이션을 시작합니다.
TapHandler {
onTapped: {
if (qmlPlaneAnimation.running) {
console.log("Plane still in the air.");
return;
}
if (oslo2Berlin.position === berlin) {
coordinateAnimation.from = berlin;
coordinateAnimation.to = oslo;
} else if (oslo2Berlin.position === oslo) {
coordinateAnimation.from = oslo;
coordinateAnimation.to = berlin;
}
qmlPlaneAnimation.rotationDirection = oslo2Berlin.position.azimuthTo(coordinateAnimation.to)
qmlPlaneAnimation.start()
}
}© 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.