Ошибка cannot create children for a parent that is in a different thread

I am using Qt 4.6.0 (32 bit) under Windows 7 Ultimate. Consider the following QThread:

Interface

class ResultThread : public QThread
{
Q_OBJECT

    QString _post_data;
    QNetworkAccessManager _net_acc_mgr;

signals:
    void onFinished(QNetworkReply* net_reply);

private slots:
    void onReplyFinished(QNetworkReply* net_reply);

public:
    ResultThread();

    void run();
    void setPostData(const QString& post_data);
};

Implementation

ResultThread::ResultThread() : _net_acc_mgr(this)
{
    connect(&_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(onReplyFinished(QNetworkReply*)));
}

void ResultThread::onReplyFinished(QNetworkReply* net_reply)
{
    emit onFinished(net_reply);
}

void ResultThread::setPostData(const QString& post_data)
{
    _post_data = post_data;
}

void ResultThread::run()
{
    _net_acc_mgr.post(QNetworkRequest(QUrl("http://[omitted]")),
                      QByteArray(_post_data.toStdString().c_str()));
}

Whenever _net_acc_mgr.post() is executed in ResultThread::run(), I got the following Application Output in Qt Creator:

QObject: Cannot create children for a parent that is in a different thread.

(Parent is QNetworkAccessManager(0x22fe58), parent’s thread is QThread(0x9284190), current thread is ResultThread(0x22fe48)

What does this mean? How to solve it?

EDIT:

I tried doing what you guys told me in comments … :

Citizen * c = new Citizen(this);

QThread thread;
c->moveToThread(&thread);

connect(&thread, SIGNAL(started()), c, SLOT(ProcessActions()));
thread.start();

This produces even more errors:

QThread: Destroyed while thread is still running
ASSERT failure in QThread::setTerminationEnabled(): "Current thread was not started with QThread.", file c:ndk_buildreposqt-desktopsrccorelibthreadqthread_win.cpp, line 542
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
QObject::killTimers: timers cannot be stopped from another thread

I am having problems with this error … I’m stuck on this for 2 days already and can’t get a solution.

Header:

class Citizen : public QThread
{
Q_OBJECT    
    QNetworkAccessManager * manager;

private slots:
    void onReplyFinished(QNetworkReply* net_reply);

public:
    Citizen(QObject * parent);

    void run();
};

Implementation:

Citizen::Citizen(QObject * parent)
{
    manager = new QNetworkAccessManager;
    connect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(onReplyFinished(QNetworkReply*)));
}

void Citizen::onReplyFinished(QNetworkReply* net_reply)
{
    emit onFinished(net_reply);
}

void Citizen::run()
{
    manager->get(QNetworkRequest(QUrl("http://google.com"));

    QEventLoop eLoop;
    connect(manager, SIGNAL( finished( QNetworkReply * ) ), &eLoop, SLOT(quit()));
    eLoop.exec(QEventLoop::ExcludeUserInputEvents);

    qDebug() << "loaded google!";

    exec();
}

When manager->get() gets executed, I get the following error:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0xc996cf8), parent's thread is QThread(0xaba48d8), current thread is Citizen(0xca7ae08)

When eLoop.exec() gets executed:

QObject::startTimer: timers cannot be started from another thread

I start this thread in the following manner:

Citizen * c = new Citizen(this);
c->start();

Why does this happen? How to solve this?

Issue

When I try to change the text of a text browser which is inside a scroll area I get this PyQt5 threading error:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x212e3bb1f50), parent's thread is QThread(0x212e171e220), current thread is QThread(0x212e41dc7e0)

I assume it is because of the scroll area and that I don’t have access to it from the thread I am trying to change this from and it works if I put the same bit of code…

filepath = "..."
with open(filepath, "r") as f:
    contents = f.read()
    #print(contents)
    self.log_1.setText(contents)

(and yes I am aware that the filepath is «…», used for file security.)
…inside the thread that the scrollarea is created inside it works completely fine.

The only thing I don’t know is how to fix this. I think you might be able to inherit the thread to the scroll area in someway, idk.

My code, but simplified:

from PyQt5 import QtCore, QtGui, QtWidgets
from mcstatus import MinecraftServer
import threading

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1379, 523)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.scrollArea_1 = QtWidgets.QScrollArea(self.S1)
        self.scrollArea_1.setGeometry(QtCore.QRect(0, 20, 981, 341))
        self.scrollArea_1.setWidgetResizable(True)
        self.scrollArea_1.setObjectName("scrollArea_1")
        self.scrollAreaWidgetContents_1 = QtWidgets.QWidget()
        self.scrollAreaWidgetContents_1.setGeometry(QtCore.QRect(0, 0, 979, 339))
        self.scrollAreaWidgetContents_1.setObjectName("scrollAreaWidgetContents_1")
        self.log_1 = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents_1)
        self.log_1.setGeometry(QtCore.QRect(0, 0, 981, 341))
        self.log_1.setMinimumSize(QtCore.QSize(981, 341))
        self.log_1.viewport().setProperty("cursor", 
        QtGui.QCursor(QtCore.Qt.IBeamCursor))
        self.log_1.setObjectName("log_1")
        self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1)

    def update1(self, MainWindow):

        threading.Timer(0.2, self.update1, {MainWindow: MainWindow}).start()

        ip = "..."
        port = 25565 #Server 1
        server = MinecraftServer(ip, port)

        try:



            filepath = "..."
            with open(filepath, "r") as f:
                contents = f.read()
                #print(contents)
                self.log_1.setText(contents)



        except IOError as e:

            self.StatusL_1.setText(self.translate("MainWindow", "<html><head/><body><p><span style=" font-size:18pt;">Status: Off</span></p></body></html>"))
        else:
            self.StatusL_1.setText(self.translate("MainWindow", "<html><head/><body><p><span style=" font-size:18pt;">Status: On</span></p></body></html>"))

Solution

You should not directly modify the GUI from another thread, one way to modify the GUI indirectly from another thread is to use the Qt signals:

import threading
from PyQt5 import QtCore, QtGui, QtWidgets
from mcstatus import MinecraftServer


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # ...


class Worker(QtCore.QObject):
    logged = QtCore.pyqtSignal(str)
    statusChanged = QtCore.pyqtSignal(bool)

    def start(self):
        threading.Timer(0.2, self._execute, daemon=True).start()

    def _execute(self):
        threading.Timer(0.2, self._execute, daemon=True).start()
        ip = "..."
        port = 25565  # Server 1
        server = MinecraftServer(ip, port)

        try:
            filepath = "..."
            with open(filepath, "r") as f:
                contents = f.read()
                self.logged.emit(contents)
        except IOError as e:
            self.statusChanged.emit(False)
        else:
            self.statusChanged.emit(True)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        self.worker = Worker()
        self.worker.logged.connect(self.log_1.setText)
        self.worker.statusChanged.connect(self.on_status_changed)
        self.worker.start()

    @QtCore.pyqtSlot(bool)
    def on_status_changed(self, status):
        text = '<html><head/><body><p><span style=" font-size:18pt;">Status: {}</span></p></body></html>'.format(
            "On" if status else "Off"
        )
        self.StatusL_1.setText(text)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

Answered By — eyllanesc

Автор Тема: QObject: Cannot create children for a parent that is in a different thread.  (Прочитано 10173 раз)
Alex_C

Гость


Делаю QTcpServer. Возникает ошибка
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x3ef148), parent’s thread is CatTcpThread(0x3ea288), current thread is QThread(0x3e5250)

Приведу код:

h-фаил

C++ (Qt)

class CatTcpThread : public QThread
{
   Q_OBJECT
public:
   CatTcpThread(int socketDescription, QObject *parent=0);
   void run();
   QHostAddress GetAddressClient();

 private:
   int m_socketDescriptor;
   QTcpSocket *m_client;
   QHostAddress m_addr;

 public slots:
   void slot_onRead();
   void slot_onDisconnect();
   void slot_onGetError(QAbstractSocket::SocketError error);
   void slot_transmiteDataForClient(QByteArray &);

 signals:
   void signal_setup();
   void signal_startCat(int);
   void signal_endWork();
   void signal_error(QAbstractSocket::SocketError);
};

 //******************************************************************************
// CatTcpServer
//******************************************************************************

 class CatTcpServer : public QTcpServer
{
   Q_OBJECT
public:
   CatTcpServer(const QString &iniFileName, QObject *parent = 0);
   ~CatTcpServer();
   bool startServer();
   void writeData(QByteArray &block, QString ip);
   void writeData(QByteArray &block);

 protected:
   void removeAllThreads();

     void incomingConnection(int socketDescriptor);
   QList<CatTcpThread*> m_threads;

 public slots:
   void slot_removeThread();
   void slot_setup();
   void slot_startCat(int);
   void slot_endWork();

 signals:
   void signal_transmiteDataForClient(QByteArray &);
private:
   ...

 };

cpp-файл

C++ (Qt)

void CatTcpThread::run()
{
   QByteArray block;
   m_client = new QTcpSocket();
   QHostAddress local;

     if(!m_client->setSocketDescriptor(m_socketDescriptor))
   {
       return;
   }

     connect(m_client, SIGNAL(readyRead()),
           this, SLOT(slot_onRead()), Qt::DirectConnection);
   connect(m_client, SIGNAL(disconnected()),
           this, SLOT(slot_onDisconnect()), Qt::DirectConnection);
   connect(m_client, SIGNAL(error(QAbstractSocket::SocketError)),
           this, SLOT(slot_onGetError(QAbstractSocket::SocketError)),
           Qt::DirectConnection);

     m_addr = m_client->peerAddress();

     exec();
}

 // Отправить данные клиенту
void CatTcpThread::slot_transmiteDataForClient(QByteArray &block)
{
   if(block.size() > 0)
   {
       if(m_client->write(block) < 0)
           return;

         if(!m_client->waitForBytesWritten(3000))
           return;
   }
}

 //******************************************************************************
// CatTcpServer
//******************************************************************************

 void CatTcpServer::writeData(QByteArray &block)
{
//    emit signal_transmiteDataForClient(block);
   for(int i = 0; i < m_threads.count(); i++)
   {
       // Ошибка возникает вот тут!!!
       m_threads[i]->slot_transmiteDataForClient(block);
   }
}

Т.е. ошибка в ф-ции void CatTcpThread::slot_transmiteDataForClient(QByteArray &block), а точнее вот тут
m_client->write(block)
как я понимаю, это из-за того, что m_client создается в другом потоке. Вопрос — как правильно переделать  CatTcpThread? Создавать m_client не в run()?


Записан
Alex_C

Гость


В общем проблему решил сам

C++ (Qt)

void CatTcpServer::incomingConnection(int socketDescriptor)
{
   qDebug() << "Incomming connection!";

     CatTcpThread *thread = new CatTcpThread(socketDescriptor, this);
   connect(thread, SIGNAL(finished()), this, SLOT(slot_removeThread()),
           Qt::DirectConnection);
   connect(thread, SIGNAL(signal_setup()), this, SLOT(slot_setup()));
   connect(thread, SIGNAL(signal_startCat(int)), this, SLOT(slot_startCat(int)));
   connect(thread, SIGNAL(signal_endWork()), this, SLOT(slot_endWork()));
   connect(this, SIGNAL(signal_transmiteDataForClient(QByteArray &)),
           thread, SLOT(slot_transmiteDataForClient(QByteArray&)));
   m_threads.append(thread);

     this->moveToThread(thread);  <- Вот эту строку добавил
   thread->start();
}

Решение нашел в интернете. А теперь подскажите (не до конца понял), что делает moveToThread?


Записан
mutineer

Гость


Перемещает наследника QObject в другой тред, то есть объект теперь работает не с главным QEventLoop, а с QEventLoop треда


Записан
Alex_C

Гость


Спасибо за ответ!
Но выходит я не правильно сделал.
this->moveToThread(thread);
работает, только если один клиент подключен. При подключении второго клиента, он начинает работать, что верно — теперь же для него
this->moveToThread(thread);
а первый выдает ошибку.
А как выйти из этого положения?


Записан
V1KT0P

Гость


Спасибо за ответ!
Но выходит я не правильно сделал.
this->moveToThread(thread);
работает, только если один клиент подключен. При подключении второго клиента, он начинает работать, что верно — теперь же для него
this->moveToThread(thread);
а первый выдает ошибку.
А как выйти из этого положения?

Если я правильно понял, то сперва у тебя была ошибка из-за того что ты лез в объект принадлежащий чужому треду. Ты решил это переносом сервера в тред в котором находится этот объект. Тут ты создаешь еще один объект и снова начинаешь переносить сервер в уже друго тред(это уже начинает плохо попахивать). И что же ты получаешь, ведь теперь при обращении к первому объекту получится таже ситуация что и до перемещения треда ибо сервер уже работает в треде другого объекта.
Зачем ты делаешь это:

C++ (Qt)

m_threads[i]->slot_transmiteDataForClient(block);

Если первоначально ты делал правильно:

C++ (Qt)

emit signal_transmiteDataForClient(block);

Ибо если объект принадлежит треду то и вызывать его лучше из этого треда. А постоянно перемещать сервер между разными тредами я считаю не такой уж хорошей идеей.


Записан
Alex_C

Гость


Без разницы. Что emit, что циклом проходить по всем тредам — результат один и тот же.
Пока понять не могу следующее — проблема возникает тут

C++ (Qt)

void CatTcpThread::slot_transmiteDataForClient(QByteArray block)
{
   if(block.size() > 0)
   {
       if(m_client->write(block) < 0)  <- вот тут возникает эта ошибка
           return;
       if(!m_client->waitForBytesWritten(3000))
           return;
   }
}
 

Запустил три клиента — сервер выдал на каждого клиента по одной такой ошибке… и прекрасно работал! Это как то странно…

И за одно еще одна не понятка. У меня

C++ (Qt)

void CatTcpServer::incomingConnection(int socketDescriptor)
{
   qDebug() << "Incomming connection!";

     CatTcpThread *thread = new CatTcpThread(socketDescriptor, this);
   connect(thread, SIGNAL(finished()), this, SLOT(slot_removeThread()),
           Qt::DirectConnection);
   connect(thread, SIGNAL(signal_setup()), this, SLOT(slot_setup()));
   connect(thread, SIGNAL(signal_startCat(int)), this, SLOT(slot_startCat(int)));
   connect(thread, SIGNAL(signal_endWork()), this, SLOT(slot_endWork()));
   connect(this, SIGNAL(signal_transmiteDataForClient(QByteArray)),
           thread, SLOT(slot_transmiteDataForClient(QByteArray)));
   m_threads.append(thread);

     thread->start();
}

 void CatTcpServer::slot_removeThread()
{
   qDebug() << "Remove thread";
   CatTcpThread *thread = qobject_cast<CatTcpThread*>(sender());
   m_threads.removeAt(m_threads.indexOf(thread));
   thread->deleteLater();
}

когда я закрываю клиента (просто выхожу из программы), как серверу узнать, что клиента больше нет?


Записан
Alex_C

Гость


Да и конечно moveToThread была совсем не правильной идеей.


Записан
V1KT0P

Гость


Ну смотри вот ошибка:

Parent is QNativeSocketEngine(0x3ef148), parent’s thread is CatTcpThread(0x3ea288), current thread is QThread(0x3e5250)

В ней сказано что QNativeSocketEngine принадлежит потоку CatTcpThread(указатель на 0x3ea288), но вызывается из другого потока указатель которого 0x3e5250. То-есть ты вызываешь метод объекта не из того потока к которому он принадлежит.
Вызывай методы CatTcpThread только через сигналы.
Для того чтоб серверу знать какой клиент разорвал связь:
1) Создаешь у CatTcpServer слот который будет вызываться когда CatTcpThread разрывает связь. У CatTcpThread для этого делаешь сигнал.
2) Соединяешь сигнал CatTcpThread с слотом CatTcpServer через QSignalMapper, для того чтоб знать какой именно CatTcpThread вызвал этот слот.


Записан
Alex_C

Гость


Так я и вызываю через сигналы…
Нет, тут все не так просто. Я уже прогуглил интернет — аналогичных вопросов много, ответов — нет.
Можно конечно отказаться от использования отдельных тредов для каждого соединения.
Такое впечатление, что пример threadedfortuneserver из Qt только и работает так, как он там и сделан…


Записан
V1KT0P

Гость


Так я и вызываю через сигналы…
Нет, тут все не так просто. Я уже прогуглил интернет — аналогичных вопросов много, ответов — нет.
Можно конечно отказаться от использования отдельных тредов для каждого соединения.
Такое впечатление, что пример threadedfortuneserver из Qt только и работает так, как он там и сделан…

Дело в том, что я сам лично делал сервер в котором каждый клиент обрабатывался в своем потоке и все работало как часы.
Но я всегда на всякий случай при коннекте указываю Qt::QueuedConnection. Попробуй и ты, может коннекты неправильно соединяются.


Записан
Alex_C

Гость


Да конечно Qt::QueuedConnection пробовал в первую очередь.

В общем так — переделал без отдельных тредов

C++ (Qt)

void CatTcpServer::incomingConnection(int socketDescriptor)
{
   //    CatTcpThread *thread = new CatTcpThread(socketDescriptor, this);
   //    connect(thread, SIGNAL(finished()), this, SLOT(slot_removeThread()),
   //            Qt::DirectConnection);
   //    connect(thread, SIGNAL(signal_setup()), this, SLOT(slot_setup()));
   //    connect(thread, SIGNAL(signal_startCat(int)), this, SLOT(slot_startCat(int)));
   //    connect(thread, SIGNAL(signal_endWork()), this, SLOT(slot_endWork()));
   //    connect(this, SIGNAL(signal_transmiteDataForClient(QByteArray)),
   //            thread, SLOT(slot_transmiteDataForClient(QByteArray)));
   //    m_threads.append(thread);

     //    thread->start();

     QTcpSocket *clientConnection = new QTcpSocket();
   clientConnection->setSocketDescriptor(socketDescriptor);

     qDebug() << "Incomming connection!";
   clientConnections.append(clientConnection);
   connect(clientConnection, SIGNAL(readyRead()),
           this, SLOT(slot_onRead()), Qt::DirectConnection);
   connect(clientConnection, SIGNAL(disconnected()),
           this, SLOT(slot_onDisconnect()));
}

То, что закомментированно — как было. Без тредов работает на ура.
Опять же по гуглу — все ответы на подобные вопросы сводились к этому же — делать без отдельных тредов. На сколько я понимаю, проблема здесь в том, что QTcpSocked изначально использует свою тред.  Но все же очень был бы раз понять, почему с тредами не работает, если даже сами разработчики пример с тредом приводят. Хотя конечно тот пример , что у них — принципиально другой.


Записан

August 03, 2012 by Andy Shaw
| Comments

After an unfortunately long hiatus mainly due to the vacation period we are finally back and hopefully back on track with having weekly updates. So without further delay then here is this week’s support weekly.

As most of us, if not all of us, would have encountered by now when running Qt applications, warning or error messages in the debug output, some are straightforward to understand and some are a bit more cryptic. So this week I will go over some of the warning messages that may come up that aren’t necessarily the easiest to make sense of and go into some detail as to what causes the warning and how to fix it.

Initializing QFontEngineQPF failed for /app/lib/fonts/fontname.ttf

This is an indication that the application cannot handle ttf fonts, you may see this message more than once as it will occur for all the ttf fonts that it finds. In order to fix this then the system needs to have FreeType made available so that Qt can be configured with it (it will auto-detect it so as long as it is installed and in place it should pick it up). Alternatively, you can configure with -qt-freetype and it will use the version of FreeType that is bundled with the Qt package.

QPainter::begin: Paint device returned engine == 0, type: 2

When you get a message from QPainter saying that the engine returned 0, this means that for some reason it is unable to paint on the paint device in question. Pretty much all the times you see this it would be in relation to a QPixmap or a QImage. In the case of QPixmap/QImage it means that it is null, meaning that it has no size set which is why it cannot paint to it. This is either because no size was specified for it or because the size requested was too big that it would have caused an overflow which meant that it ended up not having that size set. If you call isNull() then most of the time it will return true, but sometimes it is possible that QPixmap will not return true but will still have a problem if the size of the pixmap was too large. Reducing the size will make it work.

QObject: Cannot create children for a parent that is in a different thread. (Parent is …, parent’s thread is …, current thread is …)

This one is a bit more understandable at least, but sometimes it is still not clear as to why it occurs. The key problem is that when you want to use objects inside another thread then they have to be created inside the run() function of the thread in question. For example in this case, the warning would be triggered if the object was created in the main() function (thus making it owned by the main thread) and then when used in the other thread it created a child object. Another way that this could be triggered is that the object is created inside the run() function but has been given a parent object which means that the parent and the child belong to different threads. The way to solve this is to either create the objects inside the run() function or to ensure moveToThread() is called on the object before using it inside the run() function.

locking qpf: Permission denied

This is a problem that can come up with Qt for Embedded Linux applications when they are deployed, basically it is not able to lock a font file for usage. The reason this happens is because NFS is not enabled for the device, if NFS is enabled for the device then it should solve this problem.

If anyone has any other warnings that are not clear then please feel free to let me know in the comments, if there are enough then I can do a follow up post explaining some more. Alternatively you can always contact Qt Commercial Support at http://qt.digia.com/customerportal and ask us directly if you encounter one that you don’t know what to do about.

Finally, I would like to thank everyone who gave feedback about support in the recent customer survey, I will be going through all the results carefully and we will be taking all the feedback on board and will use this to improve on our service to you. And if you ever want to give us further feedback you can either do so by sending it via the customer portal (where I will see it) or if you prefer you can send it via your account manager in Qt Commercial Sales who will pass it on to me.

Until next time, happy coding!

  • Ошибка cannot create 3d device
  • Ошибка cannot continue without model
  • Ошибка cannot call member function without object
  • Ошибка cannot access java lang string
  • Ошибка canary destiny 2