飞机制志者(QML)

Plane Spotter 示例演示了将位置和定位数据类型紧密集成到 QML 的过程中。

Plane Spotter 示例演示了如何将位置和定位相关的 C++ 数据类型集成到 QML,反之亦然。当希望在本地环境中运行计算密集型的位置计算但在 QML 中显示结果时,这非常有用。

示例显示了一个欧洲的地图和两条横跨欧洲的航线上的飞机。第一架飞机在奥斯陆和柏林之间通勤,第二架飞机在伦敦和柏林之间通勤。每架飞机的位置跟踪是在 C++ 中实现的。奥斯陆-柏林飞机由 QML 控制,而伦敦-柏林飞机由 C++ 驾驶员控制。

运行示例

要从 Qt Creator 运行示例,打开 欢迎 模式并从 示例 中选择示例。更多信息,请访问 构建和运行示例

概述

此示例使用 Q_GADGET 功能作为其位置控制器实现的一部分。它允许 直接集成QObject 的 C++ 值类型到 QML 中。

PlaneController 类的主要目的是在给定时间跟踪飞机的当前位置。它通过其位置属性公开位置。

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 非常相似,它可以逐个动画化从地理位置到另一个地理位置的过渡。此示例演示了如何使用 C++ 代码和 PlaneController 自身的驾驶能力及使用 CoordinateAnimation 作为驾驶员的 QML 代码修改 PlaneController 的位置属性。奥斯陆-柏林飞机使用 QML 代码进行动画,伦敦-柏林飞机使用 C++ 代码进行动画。

无论使用哪种驾驶员,驾驶员的操作结果在C++和QML中都可见,因此示例展示了在C++/QML边界之间无阻碍的直接位置数据交换。

使用MapQuickItem类型生成每个Plane的视觉表示,这允许将任意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 {
        //...
    }
}
cpp驾驶员

C++飞机由C++控制。控制器类的fromto属性设置驾驶员使用的起点和终点,以计算飞机的方位角

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

驾驶员使用QBasicTimerQTimerEvents来不断更新位置。在每个计时器迭代过程中调用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()
    }
}

示例项目 @ code.qt.io

© 2024 Qt公司有限公司。此处包含的文档贡献是相应所有者的版权。此处提供的文档是根据免费软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt及其相关标志是芬兰和/或世界各地的Qt公司有限公司的商标。所有其他商标均为其相应所有者的财产。