我在我的 Qt 5.7(在 Windows 10 上)应用程序中遇到了一个奇怪的错误,并且找不到这种行为的常见罪魁祸首:
- 被移动的对象有一个父对象——当然不是这样
- 试图将对象拉到线程而不是推送它 - 这是错误的原因但是我不知道它来自哪里
完整的错误信息是
QObject::moveToThread: 当前线程 (0x2afcca68) 不是对象的线程 (0x34f4acc8)。无法移动到目标线程 (0x34f4adc8)
QObject::setParent:无法设置父级,新父级在不同的线程中
这也是我的代码:
主文件
#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(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);
}
正如你所看到的,我这里有一个典型的工作线程。我已经确定我的工人(这里DeviceMapper
)没有父母。它也在我的小部件(QThread
也创建了)内部实例化,但与计时器一起移动到线程。
现在除了标题中的明显问题之外,我还必须提及以下内容:
this->timer->moveToThread(this->deviceThread);
调用时没有这样的错误- 这个相同的代码在另一个项目中没有任何问题,这是一个 subdirs 项目 - 一个子项目创建共享库(我也在这个项目中使用)和另一个 - 使用该库的应用程序。
我的另一个应用程序和这个应用程序之间的唯一区别是QQuickWidget
(而不是QWidget
)和QML
. 我很陌生QML
,这也是我的第一次QQuickWidget
,所以我可能会错过一些需要“激活”的明显设置。
我也加了
cout << this->deviceWorker->thread()->currentThreadId() << endl;
cout << this->thread()->currentThreadId() << endl;
就在之前this->deviceWorker->moveToThread(this->deviceThread);
,我得到了
0x18b0
0x18b0
这意味着在moveToThread(...)
我的对象属于QThread
实例化的同一个线程之前。在返回相同的结果之后打印线程 ID,moveToThread(...)
但这是由于未能将对象正确移动到另一个线程而导致的。
更新:
错误消息仅在以发布模式构建时出现,但无论构建类型如何,我的错误仍然存在。