You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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_())

关键修改说明

  1. 将服务器逻辑移到独立线程:点击单选按钮时创建并启动子线程执行activate_server,主线程不会被阻塞,UI能正常响应操作和刷新。
  2. 使用QMetaObject.invokeMethod更新UI:这是Qt官方推荐的跨线程更新UI的安全方式,通过QueuedConnection将更新请求放到主线程的事件队列中执行,完全符合Qt的线程规则。
  3. 修正线程创建逻辑:target=self.activate_server传递函数对象而非直接调用,去掉join()避免阻塞主线程,设置daemon=True让子线程随主线程退出,防止程序残留后台进程。

内容的提问来源于stack exchange,提问作者Nir Eyal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:01:12