Issue
I wrote an echo server with sockets In this way, the user clicks on join and connects to the server
Then he clicks on the send button to send and receive a text which is a string with 'message'
value
Everything works fine
When the user clicks on the join button, he is correctly connected to the server
When he clicks on the send button, the message is correctly sent to the server, and it is correctly echoed from the server and comes to the client
And I can easily print the message
in add_message function
The problem is that when I want to create a label from the message and put it in self.contentWidgetLayout
in add_message function
, I get this error ->
QObject::setParent: Cannot set parent, new parent is in a different thread
client.py
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import socket
import threading
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.resize(600,600)
self.show()
#central widget
centWidget = QWidget()
self.centWidgetLayout = QVBoxLayout()
self.centWidgetLayout.setSpacing(0)
self.centWidgetLayout.setContentsMargins(0,0,0,0)
centWidget.setLayout(self.centWidgetLayout)
self.setCentralWidget(centWidget)
#join
self.join = QPushButton('join')
self.join.clicked.connect(self.connect)
self.centWidgetLayout.addWidget(self.join)
#message box
self.contentWidget = QWidget()
self.contentWidgetLayout = QVBoxLayout()
self.contentWidget.setLayout(self.contentWidgetLayout)
self.centWidgetLayout.addWidget(self.contentWidget)
self.contentWidget.setFixedHeight(330)
#send message
self.button = QPushButton('send')
self.centWidgetLayout.addWidget(self.button)
self.button.clicked.connect(self.send_message)
def add_message(self,message):
print(message)
self.msg = QLabel(message)
self.contentWidgetLayout.addWidget(self.msg)
def connect(self):
self.HOST = '127.0.0.1'
self.PORT = 1234
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client.connect((self.HOST, self.PORT))
self.add_message("[SERVER] Successfully connected to the server")
except:
print('Error')
username = 'john'
if username != '':
self.client.sendall(username.encode())
else:
print('Error')
threading.Thread(target=self.listen_for_messages_from_server, args=(self.client, )).start()
def send_message(self):
message = 'message'
if message != '':
self.client.sendall(message.encode())
else:
print('error')
def listen_for_messages_from_server(self,client):
while True:
message = client.recv(2048).decode('utf-8')
if message != '':
self.add_message(message)
else:
print('Message recevied from client is empty')
app = QApplication([])
win = Window()
app.exec()
Solution
Qt widgets are not threadsafe. Qt even requires that all GUI operations run in the same thread.
Qt uses the concept of thread affinity for its objects. This means that, e.g., a QLabel created in thread 1 will "belong" to thread 1. In your case, the QLabel is created in the listening thread and it belongs there. It cannot be added as a child to any GUI element belonging to the main thread.
The resolution of your problem is to never interact with the GUI directly from the listening thread. Instead, you can emit a signal from listen_for_messages_from_server()
whenever you receive a message. This signal can be connected to a slot which creates and adds the QLabel.
To do so, ideally you have a helper object dealing with communication that derives from QObject
and defines the signals. This object can then be created in the listening thread, or moved to it. Using QThread
might help as well.
Answered By - ypnos
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.