Sur cette page

Plane Spotter (QML)

L'exemple Plane Spotter démontre l'intégration étroite des types de données de localisation et de positionnement dans QML.

L'exemple Plane Spotter montre comment intégrer les types de données C++ liés à la localisation et au positionnement dans QML et vice versa. Ceci est utile lorsqu'il est souhaitable d'exécuter des calculs de position intensifs dans des environnements natifs, mais que les résultats doivent être affichés en utilisant QML.

L'exemple montre une carte de l'Europe et des avions sur deux routes à travers l'Europe. Le premier avion fait la navette entre Oslo et Berlin et le second entre Londres et Berlin. Le suivi de la position de chaque avion est implémenté en C++. L'avion Oslo-Berlin est piloté en QML et l'avion Londres-Berlin est commandé par un pilote C++.

Exécution de l'exemple

Pour exécuter l'exemple à partir de Qt CreatorOuvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.

Vue d'ensemble

Cet exemple utilise la fonctionnalité Q_GADGET dans le cadre de la mise en œuvre de son contrôleur de position. Il permet l'intégration directe dans QML de types de valeurs C++ non basés surQObject.

L'objectif principal de la classe PlaneController est de suivre les coordonnées actuelles de l'avion à un moment donné. Elle expose la position via sa propriété position.

class PlaneController: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QGeoCoordinate position READ position WRITE setPosition NOTIFY positionChanged)
    // ...
};

La fonction main() de l'exemple est responsable de la liaison des instances de la classe PlaneController dans le contexte 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();
}

Comme les classes dérivées de QObject, QGeoCoordinate peut être intégré sans enveloppe QML supplémentaire.

Piloter les avions

Comme indiqué ci-dessus, l'objectif principal de la classe PlaneController est de suivre les positions actuelles des deux avions (Oslo-Berlin et Londres-Berlin) et de les annoncer en tant que propriété à la couche QML. Son objectif secondaire est de définir et de faire progresser un avion le long d'une trajectoire de vol donnée. Dans un sens, il peut agir comme un pilote. Cela ressemble beaucoup à CoordinateAnimation qui peut animer la transition d'une coordonnée géographique à une autre. Cet exemple montre comment la propriété de position de PlaneController est modifiée par le code C++ en utilisant les propres capacités de pilotage du PlaneController et par le code QML en utilisant CoordinateAnimation comme pilote. L'avion Oslo-Berlin est animé à l'aide du code QML et l'avion Londres-Berlin est animé à l'aide du code C++.

Quel que soit le pilote utilisé, les résultats des actions du pilote sont visibles en C++ et en QML et l'exemple démontre donc un échange direct et sans entrave de données de position à travers la frontière C++/QML.

La représentation visuelle de chaque site Plane est réalisée à l'aide du type MapQuickItem qui permet d'intégrer des éléments QtQuick arbitraires dans une carte :

// Plane.qml
MapQuickItem {
    id: plane
    property string pilotName;
    property int bearing: 0;

    anchorPoint.x: image.width/2
    anchorPoint.y: image.height/2

    sourceItem: Item {
        //...
    }
}
Le pilote C++

L'avion C++ est piloté par C++. Les propriétés from et to de la classe de contrôleur définissent l'origine et la destination que le pilote utilise pour calculer le cap de l'avion :

Q_PROPERTY(QGeoCoordinate from READ from WRITE setFrom NOTIFY fromChanged)
Q_PROPERTY(QGeoCoordinate to READ to WRITE setTo NOTIFY toChanged)

Le pilote utilise QBasicTimer et QTimerEvents pour mettre à jour en permanence la position. À chaque itération du minuteur, PlaneController::updatePosition() est appelé et une nouvelle position est calculée.

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();
}

Une fois la nouvelle position calculée, setPosition() est appelé et la notification de changement de propriété qui s'ensuit transmet la nouvelle position à la couche QML.

Le plan C++ est démarré en cliquant sur le plan :

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() calcule le relèvement en degrés d'une coordonnée à l'autre. Notez que le code ci-dessus utilise une animation QML pour lier la rotation et le changement de position en un seul flux d'animation :

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() }
}

Tout d'abord, NumberAnimation fait pivoter l'avion dans la bonne direction et une fois que c'est fait, la fonction startFlight() se charge de démarrer le changement de position de l'avion.

public slots:
    void startFlight()
    {
        if (timer.isActive())
            return;

        startTime = QTime::currentTime();
        finishTime = startTime.addMSecs(ANIMATION_DURATION);

        timer.start(15, this);
        emit departed();
    }
Le pilote QML

Le type CoordinateAnimation est utilisé pour contrôler le vol d'Oslo à Berlin et vice versa. Il remplace la fonction ScriptAction ci-dessus.

CoordinateAnimation {
    id: coordinateAnimation; duration: 5000
    target: oslo2Berlin; property: "position"
    easing.type: Easing.InOutQuad
}

Le site TapHandler de l'avion QML met en œuvre la logique de définition de la trajectoire et lance l'animation lorsque cela est nécessaire.

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()
    }
}

Exemple de projet @ code.qt.io

© 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.