车辆

管理 Qt gRPC™ 客户端与 C++gRPC 服务器之间的两个线程连接。

该示例模拟了一个车辆仪表盘,用于显示由gRPC 服务器发送的数据。

车辆示例仪表盘截图

示例代码包含以下组件:

  • vehicle_client 一个 QtgRPC 客户端应用程序,该应用程序使用qt_add_protobuf()和qt_add_grpc()这两项 CMake 函数来生成消息和服务的 Qt 代码。
  • vehicle_server 该应用程序调用 C++gRPC 插件来生成服务器代码并实现简单的服务器逻辑。

注意:您需要 安装 C++gRPC 插件。详情请参见:模块先决条件

这两个组件均使用由文件vehicleservice.proto 和navigationservice.proto 中描述的 Protobuf 模式生成的消息。

车辆服务:

message SpeedMsg {
    int32 speed = 1;
}

message RpmMsg {
    int32 rpm = 1;
}

message AutonomyMsg {
    int32 autonomy = 1;
}

service VehicleService {
    rpc getSpeedStream(google.protobuf.Empty) returns (stream SpeedMsg) {}
    rpc getRpmStream(google.protobuf.Empty) returns (stream RpmMsg) {}
    rpc getAutonomy(google.protobuf.Empty) returns (AutonomyMsg) {}
}

导航服务:

enum DirectionEnum {
    RIGHT = 0;
    LEFT = 1;
    STRAIGHT = 2;
    BACKWARD = 3;
}

message NavigationMsg {
    int32 totalDistance = 1;
    int32 traveledDistance = 2;
    DirectionEnum direction = 3;
    string street = 4;
}

service NavigationService {
    rpc getNavigationStream(google.protobuf.Empty) returns (stream NavigationMsg) {}
}

VehicleManager 的C++单例类使用两个QThread 实例来并行与服务器通信。这些线程具有不同的gRPC 连接类型。在此示例中,共有两种类型:

  • 服务器流式 RPC,例如车辆线程的速度流。它使用两个回调函数:QGrpcServerStream::messageReceived 和QGrpcOperation::finished
    Empty speedRequest;
    m_streamSpeed= m_client->getSpeedStream(speedRequest);
    
    connect(m_streamSpeed.get(), &QGrpcServerStream::messageReceived, this, [this]() {
        if(const autospeedResponse= m_streamSpeed->read<SpeedMsg>()) {
            emitspeedChanged(speedResponse->speed());
        }
    });
    
    connect(
        m_streamSpeed.get(), &QGrpcServerStream::finished, this,
        [this](constQGrpcStatus&status) {
            if(!status.isOk()) {
                autoerror=QString("流错误:无法获取速度 %1 (%2)")
                                 .arg(status.message())
                                 .arg(QVariant::fromValue(status.code()).toString());
                emitconnectionError(error);
                qWarning() << error;
               return;
            }
        },
        Qt::SingleShotConnection);
  • 一元RPCRPC操作getAutonomy 是一元RPC。它返回单一响应。该操作仅连接至QGrpcOperation::finished 信号。
    empty autonomyRequest;
    std::unique_ptr<QGrpcCallReply>autonomyReply= m_client->getAutonomy(autonomyRequest);
    const auto *autonomyReplyPtr =autonomyReply.get();
    connect(
        autonomyReplyPtr, &QGrpcCallReply::finished, this,
        [this,autonomyReply=std::move(autonomyReply)](constQGrpcStatus&status) {
            if(!status.isOk()) {
                autoerror=QString("获取自主权 %1 (%2) 时发生调用错误")
                                 .arg(status.message())
                                 .arg(QVariant::fromValue(status.code()).toString());
                emitconnectionError(error);
                qWarning() << error;
               return;
            }
    
            if(const autoautonomyMsg= autonomyReply->read<AutonomyMsg>()) {
                emitautonomyChanged(autonomyMsg->autonomy());
            }
        },
        Qt::SingleShotConnection);

客户端主窗口的界面定义在 Main.qml 文件中。它使用 QML 的 `Connections ` 类型,将 `VehicleManager `C++ 单例类的信号连接到自定义槽:

    Connections {
        target: VehicleManager

        // This slot will be executed when the VehicleManager::totalDistanceChanged
        // signal is emitted
        function onTotalDistanceChanged(distance: int): void {
            root.totalDistance = distance;
        }

        function onSpeedChanged(speed: int): void {
            root.speed = speed;
        }

        function onRpmChanged(rpm: int): void {
            root.rpm = rpm;
        }

        function onTraveledDistanceChanged(distance: int): void {
            root.traveledDistance = distance;
        }

        function onDirectionChanged(direction: int): void {
            if (direction == VehicleManager.RIGHT) {
                root.directionImageSource = "qrc:/direction_right.svg";
            } else if (direction == VehicleManager.LEFT) {
                root.directionImageSource =  "qrc:/direction_left.svg";
            } else if (direction == VehicleManager.STRAIGHT) {
                root.directionImageSource =  "qrc:/direction_straight.svg";
            } else {
                root.directionImageSource =  "";
            }
        }

收到响应后,客户端窗口会使用收到的数据更新用户界面。通过这种方式,消息可以在不同的线程中接收,并借助信号以线程安全的方式发送至客户端用户界面。

示例项目 @ 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.