Fahrzeug

Verwalten von zwei Thread-Verbindungen zwischen einem Qt-Client gRPC™ Client und einem C++- gRPC -Server.

Das Beispiel simuliert ein Fahrzeug-Armaturenbrett, das von einem „ gRPC “-Server gesendete Daten anzeigt.

Screenshot des Armaturenbretts eines Fahrzeugs (Beispiel)

Der Beispielcode umfasst die folgenden Komponenten:

  • vehicle_client Eine Qt- gRPC -Client-Anwendung, die die CMake-Funktionen `qt_add_protobuf() ` und `qt_add_grpc()` zur Generierung von Qt-Code für Nachrichten und Dienste verwendet.
  • vehicle_server Anwendung, die das C++- gRPC -Plugin aufruft, um Servercode zu generieren und einfache Serverlogik zu implementieren.

Hinweis: Das C++- gRPC -Pluginmuss installiertsein . Details findenSie hier: Modulvoraussetzungen

Beide Komponenten verwenden generierte Nachrichten aus den Protobuf-Schemas, die in den Dateien „ vehicleservice.proto “ und „ navigationservice.proto “ beschrieben sind.

Fahrzeugdienst:

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

Navigationsdienst:

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

Das C++-Singleton „ VehicleManager “ verwendet zwei „ QThread “-Instanzen, um parallel mit dem Server zu kommunizieren. Die Threads verfügen über unterschiedliche „ gRPC “-Verbindungstypen. In diesem Beispiel gibt es zwei Typen:

  • Server-Streaming-RPCs Zum Beispiel der Geschwindigkeits-Stream des Fahrzeug-Threads. Er verwendet zwei Callback-Funktionen: QGrpcServerStream::messageReceived und 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-Fehler beim Abrufen der Geschwindigkeit %1 (%2)")
                                 .arg(status.message())
                                 .arg(QVariant::fromValue(status.code()).toString());
                emit connectionError(error);
                qWarning() << error;
               return;
            }
        },
        Qt::SingleShotConnection);
  • Unäre RPCs Die RPC-Operation „ getAutonomy “ ist ein unärer RPC. Sie gibt eine einzige Antwort zurück. Sie ist ausschließlich mit dem Signal „ QGrpcOperation::finished “ verbunden.
    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("Fehler beim Abrufen der Autonomie %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);

Die Benutzeroberfläche des Client-Hauptfensters ist in der Datei „Main.qml“ definiert. Sie verwendet den QML-Typ „ Connections “, um eine Verbindung zwischen den Signalen des C++-Singletons „ VehicleManager “ und benutzerdefinierten Slots herzustellen:

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

Nach dem Empfang einer Antwort aktualisiert das Client-Fenster die Benutzeroberfläche mit den empfangenen Daten. Auf diese Weise können Nachrichten in verschiedenen Threads empfangen und dank der Signale threadsicher an die Client-Benutzeroberfläche weitergeleitet werden.

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.