Auf dieser Seite

Plane Spotter (QML)

Das Beispiel „ Plane Spotter “ veranschaulicht die enge Integration von Standort- und Positionsdatentypen in QML.

Karte von Nordeuropa mit zwei Flugzeugsymbolen mit den Bezeichnungen „QML“ und „C++“

Das Beispiel „ Plane Spotter “ veranschaulicht, wie standort- und positionsbezogene C++-Datentypen in QML integriert werden können und umgekehrt. Dies ist nützlich, wenn rechenintensive Positionsberechnungen in nativen Umgebungen durchgeführt werden sollen, die Ergebnisse jedoch mithilfe von QML angezeigt werden sollen.

Das Beispiel zeigt eine Karte von Europa und Flugzeuge auf zwei Routen quer durch Europa. Das erste Flugzeug verkehrt zwischen Oslo und Berlin, das zweite zwischen London und Berlin. Die Positionsverfolgung der einzelnen Flugzeuge ist in C++ implementiert. Das Flugzeug auf der Strecke Oslo–Berlin wird in QML gesteuert, das Flugzeug auf der Strecke London–Berlin von einem C++-Piloten.

Ausführen des Beispiels

Sie können das Beispiel wie folgt ausführen:

Übersicht

Dieses Beispiel nutzt die Funktion „ Q_GADGET “ als Teil der Implementierung des Positionsreglers. Sie ermöglicht die direkte Integration von C++-Werttypen, die nicht auf „QObject “ basieren, in QML.

Der Hauptzweck der Klasse „ PlaneController “ besteht darin, die aktuellen Koordinaten des Flugzeugs zu einem bestimmten Zeitpunkt zu verfolgen. Sie stellt die Position über ihre Eigenschaft „position“ bereit.

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

Die Funktion ` main() ` im Beispiel ist für die Einbindung der Instanzen der Klasse ` PlaneController ` in den QML-Kontext zuständig:

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

Ähnlich wie von „ QObject “ abgeleitete Klassen kann „ QGeoCoordinate “ ohne zusätzlichen QML-Wrapper integriert werden.

Steuerung der Flugzeuge

Wie oben erwähnt, besteht der Hauptzweck der Klasse „ PlaneController “ darin, die aktuellen Positionen der beiden Flugzeuge (Oslo–Berlin und London–Berlin) zu verfolgen und diese als Eigenschaft an die QML-Ebene weiterzugeben. Ihr sekundärer Zweck besteht darin, ein Flugzeug auf eine vorgegebene Flugroute zu bringen und dessen Flugfortschritt zu steuern. In gewisser Weise kann sie als Pilot fungieren. Dies ähnelt stark der Klasse „ CoordinateAnimation “, die den Übergang von einer Geokoordinate zu einer anderen animieren kann. Dieses Beispiel veranschaulicht, wie die Positionseigenschaft von „ PlaneController “ durch C++-Code unter Verwendung der eigenen Steuerungsfunktionen von „PlaneController“ sowie durch QML-Code unter Verwendung von „ CoordinateAnimation “ als Pilot verändert wird. Die Flugstrecke Oslo–Berlin wird mithilfe von QML-Code animiert, die Flugstrecke London–Berlin mithilfe von C++-Code.

Unabhängig davon, welcher Pilot verwendet wird, sind die Ergebnisse der Pilotenaktionen sowohl in C++ als auch in QML sichtbar, sodass das Beispiel den ungehinderten und direkten Austausch von Positionsdaten über die C++/QML-Grenze hinweg demonstriert.

Die visuelle Darstellung der einzelnen „ Plane “ erfolgt mithilfe des Typs „ MapQuickItem “, der die Einbettung beliebiger Elemente von „ QtQuick “ in eine Karte ermöglicht:

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

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

    sourceItem: Item {
        //...
    }
}
Der C++-Pilot

Das C++-Flugzeug wird über C++ gesteuert. Die Eigenschaften „ from “ und „ to “ der Controller-Klasse legen den Start- und Zielort fest, anhand derer der Pilot die Flugrichtung für das Flugzeug berechnet:

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

Der Pilot verwendet „ QBasicTimer “ und „ QTimerEvents “, um die Position ständig zu aktualisieren. Bei jeder Timer-Iteration wird „ PlaneController::updatePosition() “ aufgerufen und eine neue Position berechnet.

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

Sobald die neue Position berechnet ist, wird ` setPosition() ` aufgerufen, und die anschließende Änderungsbenachrichtigung der Eigenschaft übermittelt die neue Position an die QML-Ebene.

Die C++-Ebene wird durch Klicken auf die Ebene gestartet:

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() berechnet die Peilung in Grad von einer Koordinate zur anderen. Beachten Sie, dass der obige Code eine QML-Animation verwendet, um die Drehung und die Positionsänderung in einem einzigen Animationsablauf zu verknüpfen:

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

Zunächst dreht „ NumberAnimation “ das Flugzeug in die richtige Richtung, und sobald dies geschehen ist, sorgt die Funktion „ startFlight() “ dafür, dass die Positionsänderung des Flugzeugs eingeleitet wird.

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

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

        timer.start(15, this);
        emit departed();
    }
Der QML-Pilot

Der Typ „ CoordinateAnimation “ wird verwendet, um den Flug von Oslo nach Berlin und umgekehrt zu steuern. Er ersetzt die oben genannte Funktion „ ScriptAction “.

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

Der „ TapHandler “ des QML-Flugzeugs implementiert die Logik für die Kursfestlegung und startet die Animation bei Bedarf.

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

Beispielprojekt @ 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.