Qt Невозможно переместить цель в поток

Я сталкиваюсь со странной ошибкой в ​​моем приложении 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); называется
  • Этот же код работает без каких-либо проблем в другом проекте, который является проектом 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 создается экземпляр. Печать идентификатора нити после moveToThread(...) возвращает тот же результат, но это ожидается из-за неудачной попытки правильно переместить объект в другой поток.


ОБНОВИТЬ:

Сообщение об ошибке появляется ТОЛЬКО при сборке в режиме выпуска, однако, независимо от типа сборки у меня ошибка все еще присутствует.

4

Решение

Мне удалось решить мою проблему, точно определив КОГДА это происходит.

В конце прошлой недели приложение, которое я писал, неожиданно заработало, поэтому меня беспокоило, почему все, что произошло до того, я позволил этому случиться. Я не изменил ни кода библиотеки (за исключением пары комментариев в моем коде, которые, очевидно, не могут повлиять на сам код), ни C++ код моего QML приложение. Все, что я изменил, было моим QML но таким образом, что на самом деле не относится к C++ Код внизу. Единственное, что я изменил, было тип сборки. Однако я не заметил этого на прошлой неделе.

Вчера я начал работать над новым проектом. И сразу после первого запуска у меня возникла та же проблема. Это сводило меня с ума. Поэтому я начал анализировать свой код (@Kuba Ober, извините, приятель, но опубликовать полный код или даже небольшой фрагмент библиотеки невозможно, иначе я бы сделал это (даже если это пара сотен строк реального кода ( исключая такие вещи, как комментарии и пустые строки)). Я проверил и перепроверил отношения родитель-потомок, но не смог найти ничего, что могло бы дать мне хотя бы небольшую подсказку, когда и почему это происходит. Я также проанализировал стек, чтобы лучшие из моих способностей, но все напрасно.

Затем меня поразило … Я упоминал выше, что мой предыдущий проект начал работать внезапно после изменения его тип сборки. И действительно, это было источником всего зла в моей ситуации. То, как я добавляю свою библиотеку к своим проектам (исключая исходную, которая вместе с библиотекой является частью той же subdir проект), создав папку в корневом каталоге моего нового проекта под названием libs и копирование связанных вещей к нему. Теперь, когда я закончил работать над своей библиотекой и провел некоторое тестирование, я, очевидно, решил переключиться на выпустить сборку. Однако я скопировал сборка библиотеки в release режим для построения проекта в debug Режим. Итак, после нескольких перестроений и копирования библиотеки тут и там я обнаружил, что смешивание типов сборки приложения, которое использует библиотеку и саму библиотеку, приводит к этой проблеме.

Я знаю, что смешивание типов сборки плохая идея и я не так обычно, но на этот раз это просто ускользнуло из головы и было полной катастрофой. Я не знаю, что происходит внутри, когда и приложение с типом сборки X, и библиотека с типом сборки Y смешаны, но результатом в моем случае стала ошибка, которую я разместил в этой теме.

Спасибо за помощь. Я многому научился благодаря вашим комментариям! Несмотря на то, что в моем случае отладка не была необходима, вы мне благодарны. 🙂

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]