PyQt5中while循环内无法更新TextBrowser内容的问题求助
PyQt5中循环内更新UI失效的问题解决
核心问题分析
- UI线程被阻塞:点击单选按钮后调用的
activate_server在主线程执行,里面的while True循环霸占了Qt的事件循环,UI根本没时间处理刷新请求,控件更新代码自然无效。 - 线程用法错误:创建线程时
target=self.task()是直接执行了task函数,不是传递函数引用;而且self.thread1.join()会让主线程等线程结束,等于还是同步执行,完全没起到多线程的作用。 - 跨线程操作UI违规:Qt规定只能在主线程操作UI控件,子线程直接调用
self.textEdit.setText属于非法操作,会导致程序异常或无响应。
修复后的代码
import socket import threading from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1014, 920) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.radioButton = QtWidgets.QRadioButton(self.centralwidget) self.radioButton.setGeometry(QtCore.QRect(180, 180, 141, 41)) font = QtGui.QFont() font.setBold(True) font.setWeight(75) self.radioButton.setFont(font) self.radioButton.setObjectName("radioButton") self.textEdit = QtWidgets.QTextEdit(self.centralwidget) self.textEdit.setGeometry(QtCore.QRect(180, 230, 691, 411)) self.textEdit.setStyleSheet("background-color: rgb(0, 0, 0);\n" "color: rgb(0, 255, 0);") self.textEdit.setObjectName("textEdit") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(210, 80, 571, 51)) font = QtGui.QFont() font.setPointSize(16) font.setBold(True) font.setWeight(75) self.label.setFont(font) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setObjectName("label") MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.radioButton.setText(_translate("MainWindow", "Start Server")) self.label.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" color:#00557f;\">Lots of Trips Server</span></p></body></html>")) self.radioButton.clicked.connect(self.check_readio_button) def check_readio_button(self): if self.radioButton.isChecked(): # 启动服务器线程,不阻塞主线程 self.server_thread = threading.Thread(target=self.activate_server) self.server_thread.daemon = True # 主线程退出时自动结束子线程 self.server_thread.start() def activate_server(self): hostname = socket.gethostname() IPaddress = socket.gethostbyname(hostname) port = 45971 server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind((IPaddress, port)) while True: try: message, address = server_socket.recvfrom(1024) except ConnectionError: print("Connection with server is closed.") break server_socket.sendto("Thank you for connection.".encode("UTF-8"), address) answer = message.decode("UTF-8") # 通过Qt的安全方式跨线程更新UI QtCore.QMetaObject.invokeMethod( self.textEdit, "setText", QtCore.Qt.QueuedConnection, QtCore.Q_ARG(str, f"Received message: {answer}\nThis is from server thread") ) server_socket.close() if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
关键修改说明
- 将服务器逻辑移到独立线程:点击单选按钮时创建并启动子线程执行
activate_server,主线程不会被阻塞,UI能正常响应操作和刷新。 - 使用
QMetaObject.invokeMethod更新UI:这是Qt官方推荐的跨线程更新UI的安全方式,通过QueuedConnection将更新请求放到主线程的事件队列中执行,完全符合Qt的线程规则。 - 修正线程创建逻辑:
target=self.activate_server传递函数对象而非直接调用,去掉join()避免阻塞主线程,设置daemon=True让子线程随主线程退出,防止程序残留后台进程。
内容的提问来源于stack exchange,提问作者Nir Eyal
相关产品推荐
相关产品推荐

