Véhicule

Gérer deux connexions threadées entre un client Qt gRPC et un serveur C++ gRPC.

L'exemple simule un tableau de bord de véhicule qui affiche des données envoyées par un serveur gRPC.

Capture d'écran du tableau de bord d'un exemple de véhicule

Le code de l'exemple comporte les composants suivants :

  • vehicle_client Application client Qt gRPC qui utilise les fonctions CMake qt_add_protobuf() et qt_add_grpc() pour la génération du code Qt des messages et des services.
  • vehicle_server application qui appelle le plugin C++ gRPC pour générer le code du serveur et mettre en œuvre une logique de serveur simple.

Remarque : le plugin C++ gRPC doit être installé. Pour plus de détails, cliquez ici : Prérequis du module

Les deux composants utilisent les messages générés à partir des schémas protobuf décrits dans les fichiers vehicleservice.proto et navigationservice.proto.

Service des véhicules :

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

Service de navigation :

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

Le singleton C++ VehicleManager utilise deux instances QThread pour communiquer avec le serveur en parallèle. Les threads ont différents types de connexion gRPC. Dans cet exemple, il y en a deux :

  • RPC de streaming du serveur Par exemple, le flux de vitesse du thread du véhicule. Il utilise deux fonctions de rappel : QGrpcServerStream::messageReceived et QGrpcOperation::finished
    Empty speedRequest ; m_streamSpeed =  m_client->getSpeedStream(speedRequest) ; connect(m_streamSpeed.get(), &QGrpcServerStream::messageReceived, this, [this]() { if(const auto speedResponse =  m_streamSpeed->read<SpeedMsg>()) { emit speedChanged(speedResponse->speed()) ; } }) ; connect( m_streamSpeed.get(), &QGrpcServerStream::finished, this,[this](const QGrpcStatus &status) { if (!status.isOk()) { auto error = QString("Stream error fetching speed %1 (%2)") .arg(status.message()) .arg(QVariant::fromValue(status.code()).toString()) ; emit connectionError(error) ;            qWarning() << error;
               return; }},  Qt::SingleShotConnection) ;
  • RPC unaires L'opération RPC getAutonomy est une RPC unaire. Elle renvoie une seule réponse. Elle n'est connectée qu'au signal 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)](const QGrpcStatus &status) { if (!status.isOk()) { auto error = QString("Call error fetching autonomy %1 (%2)") .arg(status.message()) .arg(QVariant::fromValue(status.code()).toString()) ; emit connectionError(error) ;            qWarning() << error;
               return; } if(const auto autonomyMsg =  autonomyReply->read<AutonomyMsg>()) { emit autonomyChanged(autonomyMsg->autonomy()) ; }},  Qt::SingleShotConnection) ;

L'interface de la fenêtre principale du client est définie dans le fichier Main.qml. Elle utilise le type QML Connections afin de se connecter aux signaux du singleton VehicleManager C++ vers des slots personnalisés :

    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 =  "";
            }
        }

Après avoir reçu une réponse, la fenêtre du client met à jour l'interface utilisateur avec les données reçues. De cette manière, les messages peuvent être reçus dans différents threads et être envoyés à l'interface utilisateur du client de manière sécurisée grâce aux signaux.

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.