Qt无法将目标移至线程

时间:2016-08-09 11:57:47

标签: c++ multithreading qt qml qquickwidget

我在Qt 5.7(在Windows 10上)应用程序中遇到了一个奇怪的错误,并且无法找到这种行为的常见罪魁祸首:

  • 移动的对象有父级 - 大多数情况并非如此
  • 尝试将对象拉到线程而不是推送它 - 这就是错误的原因但是我不知道它来自哪里

完整的错误消息是

  

QObject :: moveToThread:当前线程(0x2afcca68)不是对象的   线程(0x34f4acc8)。无法移动到目标线程(0x34f4adc8)

     

QObject :: setParent:无法设置父级,新父级是不同的   螺纹

这也是我的代码:

的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.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);
  • 时没有此类错误
  • 这个相同的代码在另一个项目中没有任何问题,这是一个子项目 - 一个子项目创建共享库(我也在这个项目中使用)和另一个 - 使用该库的应用程序

我的其他应用与此应用之间的唯一区别是使用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实例化的线程之前。在moveToThread(...)返回相同结果后打印线程ID,但由于无法将对象正确地移动到另一个线程,这是预期的。

更新

仅在构建处于发布模式时出现错误消息,但无论构建类型如何,我仍然存在该错误。

1 个答案:

答案 0 :(得分:1)

我已经设法通过查明它发生的时间来解决我的问题。

在上周末,我正在写的应用程序突然开始工作,所以即使它让我感到困扰,为什么在此之前发生了所有这些我让它成为现实。我既没有更改库的代码(除了我的代码中的一些注释,这显然不会影响代码本身),也没有更改我的C++应用程序的QML代码。我改变的只是我的QML,但实际上与下面的C++代码无关。我唯一改变的是构建类型。但是上周我没有注意到这一点。

昨天我开始研究一个新项目。在第一次运行后,我遇到了同样的问题。它让我疯了。所以我开始分析我的代码(@Kuba Ober,对不起伙伴,但发布完整的代码甚至是图书馆的一小部分是不可能的,否则我会做到这一点(即使它已经成功了几百行)实际代码(不包括注释和空行等内容)。)我检查并仔细检查了亲子关系,但无法找到任何可以给我提供一些小提示的时间以及为什么会发生这种情况。我有我也尽力分析了筹码,但都是徒劳。

然后它让我感到震惊......我上面提到过,我之前的项目在更改构建类型后突然开始工作。事实上,在我的情况下,这是所有邪恶的根源。我将库添加到项目中的方式(不包括最初的库和库是同一个subdir项目的一部分)是通过在我的新项目的根目录中创建一个名为libs的文件夹并将相关的东西复制到它。现在,在我完成我的库并进行一些测试之后,我显然决定切换到发布版本。但是,我将{strong>库构建以release模式复制到debug模式中的项目构建。因此,经过几次重建并在此处复制库后,我发现混合使用该库的应用程序的构建类型和库本身会导致此问题。

我知道混合构建类型是一个糟糕的主意而我通常不会这样做,但这次它只是让我不知所措并且完全是意外。当具有X构建类型的应用程序和具有Y构建类型的库混合在一起时,我不知道内部发生了什么,但我的情况中的结果是我在此主题中发布的错误。

感谢所有帮助。我通过你的评论学到了很多东西!即使在我的情况下调试不是必要的,你也要感谢。 :)