QML call function in C++ with threads
-
@Cleiton-Bueno Then I don't understand why your UI is freezing. Did you test and saw this freezing or do you just assume it will freeze?
-
@jsulm said in QML call function in C++ with threads:
Then I don't understand why your UI is freezing. Did you test and saw this freezing or do you just assume it will freeze?
@jsulm Because functions are with
while (true) {}
with 500ms delay, time is undetermined can be short or take a long time, and this function is sending a signal to my GUi QML that already ok. -
@Cleiton-Bueno Such loops are usually a sign of bad design. Especially in event based Qt you should avoid them. Why do you want to call a blocking function from QML? You should rethink your design and try to use signals/slots.
-
@jsulm This was an example!
I have no functions while (1), but have functions that when called will process for a while, for example, have a function that when activated will be communicating via I2C and the function returns me status of the Motor, until it stops.When I do that my QML GUI hangs as solve this with signals/slots? It would be helpful, used signals/slots but not in this case.
So I thought about using QThread within the function -
@Cleiton-Bueno If you don't want to block the UI you need asynchronous communication, even with a thread. That's why I suggested to use signals/slots. For example from the UI you could call a C++ function which triggers an action and returns immediately. As soon as the result is available your C++ code emits a signal with results which is connected to your UI.
If I understood you correctly you're already using a thread but your UI is still blocking, right? How do you use that thread? You should show some code. -
Sorry about the delay @jsulm.
I removed some parts of the code by NDA but I'm trying to expose the idea.mytask.h
#ifndef MYTASK_H #define MYTASK_H #include <QObject> #include <QTcpSocket> #include <QTimer> #include <QDebug> #include <QThread> class task : public QObject { Q_OBJECT Q_PROPERTY(QString msg READ msg WRITE setMsg NOTIFY msgChanged ) ... public: task(); virtual ~task(); Q_INVOKABLE void runGetMotor(void); ... QString err; private: QTcpSocket *socket; ... private slots: void onSocketReadData(); void onSocketConnected(); void onSocketDisconnected(); void onSocketError(QAbstractSocket::SocketError); signals: void progressMotor(int progress); ... }; #endif // MYTASK_H
mytask.cpp
//your code here #include "mytask.h" /** * @brief mytask::mytask * Metodo construtor e inicializaĆ§Ć£o de variaveis e objetos privados da classe task */ mytask::mytask() { if (DEBUG_TASK) qDebug() << "Init class and socket"; /* create socket */ socket = new QTcpSocket(this); if(DEBUG_TASK) qDebug() << "Connecting signal <-> slots"; connect(socket, SIGNAL(readyRead()), this, SLOT(onSocketReadData())); connect(socket, SIGNAL(connected()), this, SLOT(onSocketConnected())); connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError))); } /** * @brief mytask::~mytask * Metodo destrutivo da classe mytask */ mytask::~mytask() { socket->close(); delete socket; } void mytask::runGoMotor(void) { ... this->runProgress(); ... } void mytask::runProgress(void) { /* Here I need to shoot this routine as Thread */ int returnLoop=255; while(returnLoop) { /* Here send/get command, process and emit signal send data to QML */ emit progressMotor(/* HERE VARIABLE WITH VALUE PROCESSED */); // Update value returnLoop or break loop QThread::sleep(1); } }
main.cpp
... // Register our component type with QML. qmlRegisterType<mytask>("com.sys.motor", 1, 0, "MyTask"); ...
main.qml
MyTask { id: task } Rectangle { ... onClicked: { task.runGoMotor(); } } Connections { target: task onProgressMotor: { console.log("Progress Motor: "+progress); //Set variable to show value } }
There are other checks and conditions to enter the while (), and at one point for the loop.
I'm auditioning QTimer starting in the constructor and it seems that met the need, something like:
MyTask::MyTask(QObject *parent) : QObject(parent) { timer = new QTimer(this); timer->setInterval(1000); connect(timer, SIGNAL(timeout()), this, SLOT(setProgressMotor())); }
The routine would be:
Click GUi QML button -> Function C ++ (runGoMotor ()), check some conditions and calls (runProgress ()), and in turn will send commands to the Socket and process the response, so to get something to close the loop.
-
@Cleiton-Bueno said in QML call function in C++ with threads:
while(returnLoop) {
/* Here send/get command, process and emit signal send data to QML /
emit progressMotor(/ HERE VARIABLE WITH VALUE PROCESSED */);
// Update value returnLoop or break loop
QThread::sleep(1);
}This while loop blocks the Qt event loop in your thread. That means: the signal will not be emitted until the loop is finished! You either should call http://doc.qt.io/qt-5/qcoreapplication.html#processEvents in the loop or, even better, get rid of this loop.
-
@jsulm said in QML call function in C++ with threads:
@Cleiton-Bueno said in QML call function in C++ with threads:
while(returnLoop) {
/* Here send/get command, process and emit signal send data to QML /
emit progressMotor(/ HERE VARIABLE WITH VALUE PROCESSED */);
// Update value returnLoop or break loop
QThread::sleep(1);
}This while loop blocks the Qt event loop in your thread. That means: the signal will not be emitted until the loop is finished!
Signals, which are a direct function call of moc-generated code, will be emitted. Events won't be processed in the thread, which means that queued connection slots in the same thread won't be called.
I agree that the forever { emit && sleep } construct is troubling.
-
@jsulm Pulling code from moc output I happen to have lying around:
For a signal
void stuffChanged(QString)
, moc generates:// SIGNAL 0 void Singleton::stuffChanged(QString _t1) { void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 0, _a); }
QMetaObject::activate() is a private API in qtbase/src/corelib/kernel/qobject.cpp. The version that takes a pointer to the static meta object eventually calls this one:
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv) { ... if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) || (c->connectionType == Qt::QueuedConnection)) { queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker); continue; ... }
The full source is a little lengthy to quote here, but check it out if you're curious. Also, you can trace the emission of a signal through to the call of a slot for an object in the same thread without a return to the event loop with the debugger.
To reiterate, signal emission is done when
emit signal()
returns. Calling of a particular slot may be pending for any queued connection. -
I agree, this while() was bothering me.
I'm using a _timer with QTimer where start() by clicking, I get the Socket data with onReadyRead() process met _timer.stop() but emito sign for the status, seems to be working well.
But I accept suggestions for improvement, but I removed the loop while()