Confusing error using TcpSocket in a thread
-
Hello,
I am making a program that will communicate with a robot controller via a TCP socket. I need to have the socket work in a different thread so that the GUI stays responsive. The thread should send signals back to the GUI when something interesting happens.I ~thought~ that I was doing this correctly but I keep getting an error that I do not understand. I created a minimal example that also has the same error. Instead of pasting hundreds of lines of code, I put them on DropBox where they can be downloaded at this link:
[link text]https://www.dropbox.com/scl/fo/n38a6xgw6thf1grj9vzuv/h?rlkey=av21gvdrwe9j5y3bw0pvns8vq&dl=0(link url)If there is another way that people on this forum like to get files please let me know.
It probably won't make much sense without looking at the program, but here is the error about "Cannot create children for a parent that is in a different thread".
![alt text]https://www.dropbox.com/scl/fi/s2duclk7z441oeavbw4xm/Minimum-Example-Error-Message.png?rlkey=31e28yemgqwtn8avd6fwq2wmo&dl=0(image url)
Thank you so much for your help.
Henry -
@HenryInUtah said in Confusing error using TcpSocket in a thread:
Hello,
I believe that connecting to the controller in a thread is necessary because the controller uses a ring buffer for positions and I have no idea when it will be sending messages to the GUI. It is not a case of traditional send & receive when the GUI wants to.
Keyboard and mouse press events can also happen spontaneously. The event loop is designed to handle these. Socket notifier events from incoming network traffic also fit in this paradigm. Look at some of the server examples, such as the fortune server
Here is a better effort at a minimal example:
As an API thing, I suggest following the convention used by Qt and Python. Symbols beginning with an uppercase letter are a type. Use a lowercase symbol to indicate an instance. self.socket, not self.Socket.
class ClientThread(QThread): def __init__(self, parent=None): QThread.__init__(self, parent) self.Signal = Communicate() self.Socket = QtNetwork.QTcpSocket() def run(self): try: self.Socket.connectToHost('127.0.0.1', 7777)
self.Socket() is allocated in the GUI thread, where ClientThread.__init__() is executing. That means that the socket's thread affinity is the GUI thread. ClientThread.run() executes in the ClientThread thread, leading to the reported mismatch.
-
@HenryInUtah said in Confusing error using TcpSocket in a thread:
Hello,
I am making a program that will communicate with a robot controller via a TCP socket. I need to have the socket work in a different thread so that the GUI stays responsive. The thread should send signals back to the GUI when something interesting happens.This is a common mistake among programmers who are new to Qt or asynchronous programming. Try using the non-blocking APIs.
I ~thought~ that I was doing this correctly but I keep getting an error that I do not understand. I created a minimal example that also has the same error. Instead of pasting hundreds of lines of code, I put them on DropBox where they can be downloaded at this link:
If the minimal example involves hundreds of lines of code, something is wrong. Are you using a well known protocol?
It probably won't make much sense without looking at the program, but here is the error about "Cannot create children for a parent that is in a different thread".
That message is created when myObject->thread() != QThread::currentThread(). For example:
QObject *o1 = new QObject; QThread thread; o1->moveToThread(&thread); QObject *o2 = new QObject(o1); // Cannot create children...
-
Hello,
I believe that connecting to the controller in a thread is necessary because the controller uses a ring buffer for positions and I have no idea when it will be sending messages to the GUI. It is not a case of traditional send & receive when the GUI wants to.
Here is a better effort at a minimal example:
import sys from PySide2.QtCore import Qt, QObject, QThread, Signal from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton from PySide2 import QtNetwork class MainWindow(QMainWindow): def __init__(self): super().__init__() self.button = QPushButton('Connect') self.setCentralWidget(self.button) self.button.clicked.connect(self.button_Clicked) self.Client = ClientThread() def button_Clicked(self): print('Clicked') self.Client.start() class Communicate(QObject): IncommingMessage = Signal(str) TheCurrentState = Signal(str) class ClientThread(QThread): def __init__(self, parent=None): QThread.__init__(self, parent) self.Signal = Communicate() self.Socket = QtNetwork.QTcpSocket() #self.Socket.readyRead.connect(self.readMessage) #self.Socket.stateChanged.connect(self.stateChanged) def readMessage(self): Data = self.Socket.readAll() self.Signal.IncommingMessage.emit(Data) def run(self): try: self.Socket.connectToHost('127.0.0.1', 7777) except Exception as Err: print(Err) def sendMessage(self, Message): self.Socket.write(bytes(Message)) def stateChanged(self, NewSocketState): self.SocketState = str(self.Socket.state()) self.Signal.TheCurrentState.emit(self.SocketState) app = QApplication(sys.argv) window = MainWindow() window.show() app.exec_()
This is the error message when pressing the button. I don't understand why asking the QTcpSocket to connect to a host is creating children - and if it is, why isn't that happening in the thread. I'm very confused...
Clicked QObject: Cannot create children for a parent that is in a different thread. (Parent is QTcpSocket(0x1d1bab69680), parent's thread is QThread(0x1d1baacbc30), current thread is ClientThread(0x1d1ba8feb80) QObject: Cannot create children for a parent that is in a different thread. (Parent is QTcpSocket(0x1d1bab69680), parent's thread is QThread(0x1d1baacbc30), current thread is ClientThread(0x1d1ba8feb80)
-
@HenryInUtah
You have a threading problem, as @jeremy_k indicated. I think you have to do something about moving theQTcpSocket
to the thread where you are servicing it, which you have not done. Before going any further, why do you need anyQThread
at all? Every time people do they often have these problems and it's not necessary anyway.Even if you do, see also https://doc.qt.io/qtforpython-6/PySide6/QtCore/QThread.html#detailed-description for the suggested WorkerThread approach.
-
@HenryInUtah said in Confusing error using TcpSocket in a thread:
Hello,
I believe that connecting to the controller in a thread is necessary because the controller uses a ring buffer for positions and I have no idea when it will be sending messages to the GUI. It is not a case of traditional send & receive when the GUI wants to.
Keyboard and mouse press events can also happen spontaneously. The event loop is designed to handle these. Socket notifier events from incoming network traffic also fit in this paradigm. Look at some of the server examples, such as the fortune server
Here is a better effort at a minimal example:
As an API thing, I suggest following the convention used by Qt and Python. Symbols beginning with an uppercase letter are a type. Use a lowercase symbol to indicate an instance. self.socket, not self.Socket.
class ClientThread(QThread): def __init__(self, parent=None): QThread.__init__(self, parent) self.Signal = Communicate() self.Socket = QtNetwork.QTcpSocket() def run(self): try: self.Socket.connectToHost('127.0.0.1', 7777)
self.Socket() is allocated in the GUI thread, where ClientThread.__init__() is executing. That means that the socket's thread affinity is the GUI thread. ClientThread.run() executes in the ClientThread thread, leading to the reported mismatch.
-
@jeremy_k
Sorry for the delay...
You are correct about the similarity to mouse and keyboard events. I'll move the socket object into the GUI class and see how it goes.I wish that I could follow examples such as the fortune server. Unfortunately, Qt thinks it is OK for document Python with C++ examples. I guess the assumption is that everyone that knows Python also knows C++.
Cheers,
Henry -
@HenryInUtah said in Confusing error using TcpSocket in a thread:
I wish that I could follow examples such as the fortune server. Unfortunately, Qt thinks it is OK for document Python with C++ examples. I guess the assumption is that everyone that knows Python also knows C++.
I'm pretty familiar with C++, and still run into the same problem from time to time.
https://code.qt.io/cgit/pyside/examples.git/ appears to be some and possibly all of the Qt core examples translated into PySide-flavored python. I glanced at the fortune server example. It doesn't have the depth of explanation that the C++ version presents.
Direct link: https://code.qt.io/cgit/pyside/examples.git/tree/examples/network/fortuneserver.py
-