I'm facing a strange bug in my Qt 5.7 (on Windows 10) application and the usual culprits for this kind of behaviour are nowhere to be found:
- Object that is moved has a parent - most certainly not the case
- Attempting to pull object to thread instead of pushing it - this is the reason for the error however I have no idea where it's coming from
The full error message is
QObject::moveToThread: Current thread (0x2afcca68) is not the object's thread (0x34f4acc8). Cannot move to target thread (0x34f4adc8)
QObject::setParent: Cannot set parent, new parent is in a different thread
and here is also my code:
main.cpp
#include <QApplication>
#include <QQuickItem>
#include "CustomQuickWidget.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
const QUrl source = QUrl(QLatin1String("qrc:/main"));
CustomQuickWidget widget(source);
return app.exec();
}
main (alias for main.qml):
// You can put any random QML content in this case really as long as it doesn't create a window since the CustomQuickWidget does that.
Rectangle {
id: window
visible: true
width: 600
height: 480
}
CustomQuickWidget.cpp
#include "CustomQuickWidget.h"
#include <QQuickItem>
CustomQuickWidget::CustomQuickWidget(const QUrl &source, QWidget *parent) : QQuickWidget(source, parent) {
// Setup the recognizer
this->airWheelRecognizer = new QAirWheelGestureRecognizer();
this->airWheelType = QGestureRecognizer::registerRecognizer(airWheelRecognizer);
// and turn on grabbing for all the supported gestures
grabGesture(airWheelType);
grabGesture(Qt::SwipeGesture);
grabGesture(Qt::TapGesture);
// Create thread and device worker
this->deviceThread = new QThread(this);
this->deviceWorker = new DeviceMapper(this, Q_NULLPTR); // NOTE: this here is NOT for parent. The constructor's signature for this class is: DeviceMapper(QObject* receiver, QList<Qt::GestureType>* gestureIDs, QObject* parent = Q_NULLPTR)
this->deviceWorker->init();
// Create timer that will trigger the data retrieval slot upon timeout
this->timer = new QTimer();
this->timer->setTimerType(Qt::PreciseTimer);
this->timer->setInterval(5);
// Move timer and device mapper to other thread
this->timer->moveToThread(this->deviceThread);
this->deviceWorker->moveToThread(this->deviceThread); // FIXME For unknown reason: QObject::moveToThread: Current thread (...) is not the object's thread. Cannot move to target thread
// Connect widget, timer and device mapper
createConnections();
// Run thread
this->deviceThread->start();
// Connect device and start data retrieval
QTimer::singleShot(0, this->deviceWorker, &(this->deviceWorker->slotToggleConnection));
QTimer::singleShot(0, this->deviceWorker, &(this->deviceWorker->slotToggleRun));
this->show();
}
CustomQuickWidget::~CustomQuickWidget()
{
if (this->deviceThread) {
this->deviceThread->quit();
this->deviceThread->wait();
}
}
void CustomQuickWidget::createConnections()
{
connect(this->timer, SIGNAL(timeout()),
this->deviceWorker, SLOT(slotRetrieveData()));
connect(this->deviceThread, SIGNAL(started()),
this->timer, SLOT(start()));
connect(this->deviceThread, SIGNAL(finished()),
this->deviceWorker, SLOT(deleteLater()));
connect(this->deviceThread, SIGNAL(finished()),
this->deviceThread, SLOT(deleteLater()));
}
bool CustomQuickWidget::event(QEvent* event) {
if (event->type() == QEvent::Gesture) {
bool res = gestureEvent(static_cast<QGestureEvent*>(event)); // Not important so not included as code here
return res;
}
return QWidget::event(event);
}
As you can see I have a typical worker-thread-thing going on here. I've made sure that my worker (here DeviceMapper
) doesn't have a parent. It is also instantiated inside my widget (where the QThread
is also created) but moved to the thread along with a timer.
Now beside the obvious issue here that is in the title I have to mention the following:
- There is no such error when
this->timer->moveToThread(this->deviceThread);
is called - This very same code works without any issue in another project, which is a subdirs project - one sub-project creates the shared library (which I'm using in this project too) and the other - an application that uses the library.
The only difference between my other application and this one is the usage of QQuickWidget
(instead of QWidget
) and QML
. I'm quite new to QML
and this is also my first QQuickWidget
so I might be missing some obvious setting that needs to be "activated".
I've also added
cout << this->deviceWorker->thread()->currentThreadId() << endl;
cout << this->thread()->currentThreadId() << endl;
right before this->deviceWorker->moveToThread(this->deviceThread);
and I got
0x18b0
0x18b0
which means that before the moveToThread(...)
my object belongs to the same thread where the QThread
is instantiated. Printing the thread ID after the moveToThread(...)
returns the same result but this is expected due to the failure to properly move the object to the other thread.
UPDATE:
The error message appears ONLY when building in release mode however no matter the type of build I have the bug is still present.