PyQt6多线程更新UI报错:QObject::setParent跨线程问题
问题原因
你遇到的错误是因为PyQt的UI组件只能由创建它们的主线程操作。你的listen_for_messages_from_server函数运行在子线程中,直接调用add_message创建QLabel并添加到布局,属于跨线程操作UI,违反了Qt的线程规则,因此触发QObject::setParent: Cannot set parent, new parent is in a different thread错误。
解决方案:使用Qt信号与槽机制
Qt的信号与槽是线程安全的,子线程可以通过发送信号通知主线程更新UI,主线程的槽函数负责实际的UI操作。
修改步骤
- 在
Window类中定义一个字符串类型的信号,用于传递收到的消息。 - 在
__init__方法中将信号与add_message槽函数绑定。 - 在子线程的
listen_for_messages_from_server函数中,不再直接调用add_message,而是发送信号。
修改后的完整代码
from PyQt6.QtWidgets import * from PyQt6.QtGui import * from PyQt6.QtCore import * import socket import threading class Window(QMainWindow): # 定义信号,用于传递收到的消息 message_received = pyqtSignal(str) def __init__(self): super().__init__() self.resize(600,600) self.show() # 将信号与add_message槽绑定 self.message_received.connect(self.add_message) #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 != '': # 发送信号,由主线程处理UI更新 self.message_received.emit(message) else: print('Message recevied from client is empty') app = QApplication([]) win = Window() app.exec()
另一种解决方案:使用QMetaObject.invokeMethod
如果不想用信号,也可以通过QMetaObject.invokeMethod直接在主线程执行add_message方法,修改listen_for_messages_from_server中的代码即可:
def listen_for_messages_from_server(self,client): while True: message = client.recv(2048).decode('utf-8') if message != '': # 调用主线程的add_message方法 QMetaObject.invokeMethod( self, "add_message", Qt.ConnectionType.QueuedConnection, Q_ARG(str, message) ) else: print('Message recevied from client is empty')
这种方式不需要额外定义信号,直接指定在主线程执行目标方法,同样能避免跨线程UI操作的错误。
内容的提问来源于stack exchange,提问作者coder
相关产品推荐
相关产品推荐

