PYQT5子线程接收服务端登录信号后操作UI窗口崩溃无响应问题
问题根因
PyQt的所有GUI控件操作必须在主线程(Qt事件循环所在线程)中执行,你当前的get_text函数运行在独立子线程中,直接在子线程调用MainWindow.show()、ChildWindow.close()、textBrowser.append()等UI操作,会触发线程安全问题导致程序无响应。
适配方案
用Qt自带的信号槽机制实现跨线程通信,子线程只负责接收数据发送信号,主线程接收信号后执行UI操作,具体修改如下(仅修改客户端代码):
步骤1:在Ui_MainWindow类中新增自定义信号
class Ui_MainWindow(QtWidgets.QMainWindow): # 新增自定义信号 login_success = QtCore.pyqtSignal() # 登录成功信号 new_message = QtCore.pyqtSignal(str, bytes) # 新消息信号,参数为消息内容、在线人数 server_error = QtCore.pyqtSignal(str) # 服务端异常信号,参数为提示文案 # 原有__init__、setupUi等代码保留
步骤2:在setupUi函数中绑定信号与槽函数
def setupUi(self): # 新增信号绑定逻辑 self.login_success.connect(self.handle_login_success) self.new_message.connect(self.handle_new_message) self.server_error.connect(self.handle_server_error) # 原有代码全部保留 self.cl = Client() # ...后面原有代码不需要改动
步骤3:新增主线程执行的UI操作槽函数
# 登录成功后UI操作 def handle_login_success(self): print("YOU GOT IN") MainWindow.show() ChildWindow.close() # 收到新消息后UI操作 def handle_new_message(self, message, count_users): print("NOPE") print(f"message from server ---> {message}") self.pushButton_2.setEnabled(False) self.textBrowser.append(message) self.label_2.setText(f"Users in online: {count_users.decode('utf-8')}") # 服务端异常后UI操作 def handle_server_error(self, tip): self.label_2.setText(tip) self.pushButton_2.setEnabled(False)
步骤4:修改get_text函数,移除直接UI操作改为发信号
def get_text(self): while True: try: data = self.cl.recv(2048) data = pickle.loads(data) print(data) if data[0] == "USER IS SIGN": # 发送登录成功信号即可,不要直接操作UI self.login_success.emit() else: message = data[1] count_users = data[0] # 发送新消息信号 self.new_message.emit(message, count_users) except EOFError: print("EOFError") self.server_error.emit("!ВЫ ЗАПУСТИЛИ !") input() except Exception as err: print(err) self.server_error.emit("!SERVER STOPPED WORK!") input()
额外注意点
- 你当前用keyboard库实现的回车发消息逻辑也运行在子线程,后续如果需要在该逻辑中操作UI,同样需要用信号槽转发
- 服务端
user_sign_in函数中登录失败分支的打印逻辑写错了,当前打印的还是USER IS SIGN,可自行修正
内容的提问来源于stack exchange,提问作者NIKITA76
相关产品推荐
相关产品推荐

