I have a Qt project (in Win10) that requires a non-member function in my MainWindow cpp file. It's a callback function that is triggered automatically from an external library when data from an attached sensor arrives. I like to display this data on a MainWindow widget (a plainTextEdit), but of course I cannot access any MainWindow elements from my callback function, and also no signal-slot concept works so far. Below is a minimal example of the problem:
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMutex>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void displayData();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
// define global variables
QMutex mutex;
bool new_data_arrived = false;
QString newDataStr;
void newData() {
// This is a callback function triggered when data from a sensor attached via USB arrives
// I want to pass the result as a QString to MainWindow::displayData() to display it on screen
mutex.lock();
new_data_arrived = true;
newDataStr = "new data";
mutex.unlock();
};
void MainWindow::displayData()
{
mutex.lock();
if (new_data_arrived == true) {
new_data_arrived = false;
ui->plainTextEdit->appendPlainText(newDatastr);
}
mutex.unlock();
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
The function MainWindow::displayData()
is then running in a loop (but that’s unimportant for my problem, so I removed it from the minimal example above…).
As mentioned, newData()
cannot access any elements of MainWindow, which is clear. The only workaround I found so far is to use global variables as shown above, to get data from the non-member function newData()
into the member function displayData()
. I also included a mutex to avoid possible share violation (no idea if that's necessary, but it doesn't harm...).
Is there a more elegant way? In principle, what I need is either a signal-slot concept, but newData()
cannot emit signals of MainWindow, or to get this
into the function newData()
, so that ui elements can be accessed.
CodePudding user response:
You should use QMetaObject::invokeMethod to call a slot of a QObject from a non QObject slot (your callback funtion).
Here is an example:
class MainWindow : public QMainWindow
{
Q_OBJECT
//...
public slots:
void displayData( QString szNewData );
};
//Cpp MainWindow.cpp file
static MainWindow * mainWindowObj = nullptr;
void setMainWindowObj( MainWindow * ptr ){
mainWindowObj = ptr;
}
void newData() {
// This is a callback function triggered when data from a sensor attached via USB arrives
// I want to pass the result as a QString to MainWindow::displayData() to display it on screen
if( mainWindowObj == nullptr )
return;
QMetaObject::invokeMethod( mainWindowObj, //pointer to MainWindow instance (see main.cpp)
"displayData", //slot name
Qt::QueuedConnection, //connection type
Q_ARG( QString, newDataStr ) ); //Param passed to the slot
};
void MainWindow::displayData( QString szNewData )
{
ui->plainTextEdit->appendPlainText( szNewData );
}
//main.cpp file or where MainWindow instance is initialized
void setMainWindowObj( MainWindow * ptr );
int main( int argc, char *argv[] ){
//....
QApplication app(argc, argv);
//....
MainWindow mainWindow;
setMainWindowObj ( &mainWindow );
//....
}