Qt 4.8 threading, QObject::setParent: Cannot set parent, new parent is in a different thread
-
I have the following class that I want to later move to a different thread to do it's work so I don't stall the UI thread.
@class ParserXYZ : public IParser
{
public:
ParserXYZ();
virtual ~ParserXYZ();
virtual void loadData(const QByteArray &data);
virtual void parse();
virtual int getStatus(); //status of parsing, success, error, etc.
virtual QList<Item*> getResult(); //result of parsing
};@My ParserXYZ is hosted in another class, as a member variable.
@class DataGatherer : public QObject
{
Q_OBJECT
public:
DataGatherer(IParser *parser, QObject parent=0);
virtual ~DataGatherer();
void start(const QByteArray &data);
signals:
void finished(QList<Item>);
private slots:
onParserWorkerFinished();
private:
IParser *m_parser;
};@I made a worker class to to wrap ParserXYZ.
@class ParserWorker : public QObject {
Q_OBJECT
public:
ParserWorker(IParser *parser, const QByteArray &data);
virtual ~ParserWorker();
signals:
void finished();
public slots:
void start();
private:
IParser *m_parser;
QByteArray m_data;
};@The methods that orchestrate everything:
@//[1]
void DataGatherer::start(const QByteArray &data)
{
QThread *thread = new QThread(this);
ParserWorker *worker = new ParserWorker(m_parser, data);
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(start()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(worker, SIGNAL(finished()), this, SLOT(onParserWorkerFinished()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
thread->start();
}//[2]
void ParserWorker::start()
{
m_parser->loadData(m_data);
m_parser->parse();
emit finished();
}//[3]
void DataGatherer::onParserWorkerFinished()
{
if (m_parser->getStatus() == 1) {
//was OK
emit finished(m_parser->getResult());
}
}@In a different part of the application, I connect to the DataGatherer's finished slot:
@connect(m_dataGatherer, SIGNAL(finished(QList<Item*>)), this, SLOT(onDataGathererFinished(QList<Item*>)));
void onDataGathererFinished(QList<Item*> items) { foreach(Item *item, items) { this->insert(item); //this adds elements to a ListView's DataModel // insert method takes ownership of each Item // alerts: QObject::setParent: Cannot set parent, new parent is in a different thread
}
}@In my case, @//[3] void DataGatherer::onParserWorkerCompleted() @ method seems to return on the background thread, not on my main thread. Later when I pump data further via signals I eventually want to add items to a UI element and the program crashes alerting that I am attempting to setParent on an element on a different thread. How can I ensure that my signals return on the main thread?
-
Are you sure that the slot is called in the background thread? In my opinion you have created some qobjects (the result) in the background thread and you are trying to re-parent them in the different thread. Look for Qt warning "QObject::setParent: Cannot set parent, new parent is in a different thread" in Qt sources, there is simple clue: "object hierarchies are constrained to a single thread".
-
Hm, well the "Item" QObjects are in fact created in the background thread (via the parser class), but without any parent. Later, in the foreach loop they are getting implicitly reparented by the "insert" method. Would not using QObjects as return values for the parser be the solution?.
[quote author="Bogdan" date="1370617157"]Are you sure that the slot is called in the background thread? In my opinion you have created some qobjects (the result) in the background thread and you are trying to re-parent them in the different thread. Look for Qt warning "QObject::setParent: Cannot set parent, new parent is in a different thread" in Qt sources, there is simple clue: "object hierarchies are constrained to a single thread".[/quote] -
bq. Would not using QObjects as return values for the parser be the solution?
Well, I hope so :) From the other side of the fence consider such thing: why would you want to couple parser result with QObject? Is this dependency really needed? When I'm thinking about parser result then I get rather some simple tokens, not heavy, full-featured QObjects.