Search code examples
qtqthreadqtconcurrent

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


Hi have need to do some socket communication from background, I used QtConcurrent::run to this, but giving me the warning.

QObject: Cannot create children for a parent that is in a different thread.
(Parent is MainWindow(0x7fff3e69f500), parent's thread is QThread(0x16f8070), current thread is QThread(0x17413d0)

Here is the code,

MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),ui(new Ui::MainWindow){
      ui->setupUi(this);
      QFuture<void> f2 = QtConcurrent::run(this,&MainWindow::checkCamStatus);
  }


 void MainWindow::checkCamStatus(){
     while(1){
       bool flag = false;
       QTcpSocket* socket = new QTcpSocket(this);
       socket->moveToThread(this->thread());
       socket->setParent(this);
       socket->connectToHost("10.0.7.112", 80);
       if(socket->waitForConnected(1000))//check for connection for i second
       {
         qDebug() << "Cam online";
       }
       else{
         qDebug() << "..............................Cam offline";
       }

       QThread::sleep(1);
     }
 }

How can I remove the warning?


Solution

  • First, what is wrong with your code:

    You cannot use this in new QTcpSocket(this), because this->thread() is not the current thread (QThread::currentThread()).

    You cannot call any function member of socket after socket->moveToThread(this->thread()), because socket->thead() is not the current thread anymore.

    This is documented here:

    Event driven objects may only be used in a single thread. Specifically, this applies to the timer mechanism and the network module. For example, you cannot start a timer or connect a socket in a thread that is not the object's thread.

    Now, what you can do to fix your code if you just want to connect a TCP socket and do nothing more is to remove any reference to this and do something like that:

    void MainWindow::checkCamStatus(){
         while(1){
           bool flag = false;
           QScopedPointer<QTcpSocket> socket(new QTcpSocket()); 
           socket->connectToHost("10.0.7.112", 80);
           if(socket->waitForConnected(1000))//check for connection for i second
           {
             qDebug() << "Cam online";
           }
           else{
             qDebug() << "..............................Cam offline";
           }
    
           QThread::sleep(1);
         }
     }
    

    But, if you want to do something else with the TCP socket like sending or receiving data, you'd be better wrapping up your code in a class:

    class CamWatcher : public QObject
    {
        Q_OBJECT
    public:
        CamWatcher(QObject *parent = 0) :
            QObject(parent),
            m_socket(new QTcpSocket(this))
        {}
        QTcpSocket *socket() { return m_socket; }
    
    public slots:
        void tryConnect() { socket->connectToHost("10.0.7.112", 80); }
    
    private slots:
        void onSocketConnected() { qDebug() << "Connected"; }
        void onSocketDisconnected()  { qDebug() << "Disconnected"; }
        void onSocketError(QAbstractSocket::SocketError socketError)
        {
            qWarning() << m_socket->errorString();
        }
    private:
        QTcpSocket *m_socket = nullptr;
    }
    

    This way you can have all the TCP stuff (monitoring, data transfer, etc.) ins a single object. You can have it in the same thread than your MainWindow, but you can also have it in another thread if needed.

    Note that if you have the CamWatcher in another thread than MainWindow, but you want to call functions of CamWatcher you have to do something like this:

    QTimer::singleshot(0, camwatcher, &CamWatcher::tryConnect);
    QMetaObject::invokeMethod(camwatcher, "tryConnect", Qt::QueuedConnection); // No compile time check, works only with slots
    QMetaObject::invokeMethod(camwatcher, &CamWatcher::tryConnect, Qt::QueuedConnection); // Requires Qt 5.10