lambdac++17qt5cross-thread

Qt updating private class data from lamba/callback


Hi I'm a newbie in Qt and C++17 but I have to update a some private data from within a nested lambda function. These values are used to update the ui trough a timer:

This's the main window class:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_connectButton_clicked();

protected:
    void timerEvent(QTimerEvent *event);

private:
    Ui::MainWindow *ui;
    mavsdk::Mavsdk mavsdk;
    std::shared_ptr<mavsdk::Telemetry> telemetry;
    std::shared_ptr<mavsdk::System> system;
    int timerId;
    double roll;
    double pitch;
    double yaw;
    double heading;
    double latitude;
    double longitude;
    double altitude;
    int messages;

    const QString mavURI = "udp://0.0.0.0:14550";
    const int telemetryFreq = 5;
    const int timerFreq = 33;

    void connectMAV();
};

And these are relevant methods:

void MainWindow::connectMAV()
{
    mavsdk::ConnectionResult conn_result = mavsdk.add_any_connection(mavURI.toStdString());

    setWindowTitle("Listening on: " + mavURI);

    mavsdk.subscribe_on_new_system
    (
        [&]()
        {
            setWindowTitle("Connected: " + mavURI);

            system = mavsdk.systems()[0];

            telemetry = std::shared_ptr<mavsdk::Telemetry>(new mavsdk::Telemetry(system));

            mavsdk::Telemetry::Result tel_action = mavsdk::Telemetry::Result::Unknown;

            do
            {
                tel_action = telemetry->set_rate_position(5);
                std::this_thread::sleep_for(std::chrono::seconds(telemetryFreq));
            } while(tel_action == mavsdk::Telemetry::Result::Timeout);

            do
            {
                tel_action = telemetry->set_rate_attitude_euler(telemetryFreq);
                std::this_thread::sleep_for(std::chrono::seconds(1));
            } while(tel_action == mavsdk::Telemetry::Result::Timeout);

            telemetry->subscribe_position([&] (mavsdk::Telemetry::Position position) {
                latitude = position.latitude_deg;
                longitude = position.longitude_deg;
                altitude = position.absolute_altitude_m;
            });

            telemetry->subscribe_attitude_euler([&] (mavsdk::Telemetry::EulerAngle attitude) {
                roll = attitude.roll_deg;
                pitch = attitude.pitch_deg;
                yaw = attitude.yaw_deg;
            });
        }
    );
}

void MainWindow::timerEvent(QTimerEvent *event){
    ui->eadi->setRoll(roll);
    ui->eadi->setPitch(pitch);
    ui->eadi->setAltitude(altitude);

    ui->eadi->redraw();
}

It seems that the main window is never updated. And debugging the code breakpoints inside inner lambdas are never triggered.


Solution

  • I finally managed to fix this issue through Qt features. First of all I need to clarify that this problem needed a minimalistic solution since lambdas are the only way to manage callbacks for this specific API (mavsdk for instance). In short I cannot change the way the callbacks are invoked.

    The above code should be modified in this way:

     mavsdk.subscribe_on_new_system
        (
            [&]()
            {
               emit newSystem();
            }
         );
    

    of course somewhere I defined:

    signals:
      void newSystem();
    

    and:

    private slots:
      void onNewSystem();
    

    And connected it to the signal above the standard way:

    connect(this, &MainWindow::newSystem, this, &MainWindow::onNewSystem)
    

    The same schema apply also to nested lambdas.

    So we got:

    signals:
     void updatePosition(mavsdk::Telemetry::Position position);
     void updateAttitude(mavsdk::Telemetry::EulerAngle attitude);
    
    private slots:
     void onPositionUpdate(mavsdk::Telemetry::Position position);
     void onAttitudeUpdate(mavsdk::Telemetry::EulerAngle attitude);
    

    It was not so difficult after all..