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:
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:
this->timer->moveToThread(this->deviceThread);
is calledThe 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.
I have managed to solve my problem by pinpointing WHEN it's happening.
At the end of last week the application I was writing started working all of a sudden so even though it bothered me why all that happened before that I let it be. I have neither changed the library's code (except for a couple of comments in my code which obviously cannot affect the code itself) nor the C++
code of my QML
application. All I have changed was my QML
but in a way that didn't actually relate to the C++
code underneath. The only thing I changed was the build type. However I didn't actually notice that last week.
Yesterday I started working on a new project. And right after doing the very first run I got the same issue. It drove me nuts. So I started analyzing my code (@Kuba Ober, sorry mate, but posting the complete code or even a small chunk of the library is not possible otherwise I would have done it (even though it's a couple of hundreds of lines of actual code (excluding stuff such as comments and empty lines)). I checked and double-checked the parent-child relationships but couldn't find anything that could give me even a small hint when and why this is happening. I have also analyzed the stack to the best of my abilities but all in vain.
Then it struck me...I've mentioned above that my previous project started working all of a sudden after changing its build type. And indeed this was the source of all evil in my situation. The way I add my library to my projects (excluding the initial one which along with the library is part of the same subdir
project) is by creating a folder in the root directory of my new project called libs
and copying the related stuff to it. Now after I finished working on my library and did some testing I obviously decided to switch to release build. However I copied a library build in release
mode to a project build in debug
mode. So after a couple of rebuilds and copying the library here and there I found out that mixing the build types of the application that uses the library and the library itself lead to this issue.
I know that mixing build types is a bad idea and I don't that normally but this time it just slipped my mind and was a total accident. I don't know what is happening internally when both the application with X build type and the library with Y build type are mixed but the result in my case was the error I have posted in this thread.
Thanks for all the help. I learned a lot through your comments! Even though debugging was not necessary in my case you have my gratitude. :)