player-none 发表于 2025-3-4 18:53:41

PyQt 在线程中改动组件导致卡死的解决方案

众所周知,使用 threading.Thread() 线程对 PyQt 组件进行改动(比如 show,setPixmap,甚至 setText 都可以触发),会导致 PyQt 窗口无响应。

但是,一些操作是必须在线程里进行的,尤其是 socket 等网络编程库。

为什么 socket 必须使用线程?

socket 需要不断地接收来自服务端 / 客户端的信息,在接收信息的同时也应该处理其他操作,但接收信息的过程 s.recv(4096) 是不可控的。

换句话说,接收的时候,只能干等着。很显然,这不现实,因为用户还需要输入信息,不能在接收到信息之前不让用户输入。

解决办法是 threading.Thread()。(我关于 threading.Thread() 的一个帖子)让 recv 作为线程来运行,这样就可以做到不干扰程序的其他操作。

悲伤地发现自己说话很像人机

如果 socket 和 PyQt 结合使用,每次接到信息就应该立即在 QTextBrowser(一般使用这个)上显示。

你会很悲伤地发现,线程里无论调用什么函数,这个函数也会在线程里运行,无法冲破线程的枷锁。

很显然,解决办法是有的,方法是使用 pyqtSignal,每次 emit 就执行 emit 的函数。

from PyQt6.QtGui import *
from PyQt6.QtCore import *
from PyQt6.QtWidgets import *

class Window(QMainWindow):
    execute = pyqtSignal(name='execute')
    def __init__(self):
      super().__init__(self)
      self.execute.connect(lambda x: x())


如上所示,当需要将“线程运行”转换为“不线程运行”时,调用 self.execute.emit(lambda: 操作) 就行了。

FishC 发表于 2025-3-4 18:54:03

解决在PyQt中线程对于UI组件改动导致卡死的问题,您已经提到了使用 pyqtSignal 和多线程的结合。以下是通过使用PyQt的信号-槽机制来解决这个问题的详细步骤:

步骤 1:定义信号

在主要类中定义一个信号。例如:


class Window(QMainWindow):
    update_ui_signal = pyqtSignal(str)


步骤 2:连接信号与槽

连接信号到您定义的一个槽函数,该函数将处理UI的更新工作。


class Window(QMainWindow):
    def __init__(self):
      super().__init__()
      self.update_ui_signal.connect(self.update_ui)

    def update_ui(self, message):
      # 这里处理UI更新,例如更新QTextBrowser的内容
      self.text_browser.append(message)


步骤 3:在线程中发出信号

在线程中完成工作后,通过发出信号来更新UI。


def run_in_thread(self):
    # 模拟从socket接收数据
    while True:
      data = receive_data_from_socket()
      # 使用 信号 来发出数据进行UI更新
      self.update_ui_signal.emit(data)


完整示例

假设有一个简单的QWidget应用程序,复杂功能都类似:


import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QTextBrowser
from PyQt6.QtCore import QThread, pyqtSignal

class Worker(QThread):
    data_received = pyqtSignal(str)

    def run(self):
      while True:
            # 模拟接收数据
            data = receive_data_from_socket()
            self.data_received.emit(data)

def receive_data_from_socket():
    # 模拟socket等待
    QThread.sleep(1)
    return "Received data"

class Window(QMainWindow):
    def __init__(self):
      super().__init__()
      
      self.text_browser = QTextBrowser(self)
      self.setCentralWidget(self.text_browser)

      # 设置线程
      self.worker = Worker()
      self.worker.data_received.connect(self.update_ui)
      self.worker.start()

    def update_ui(self, data):
      self.text_browser.append(data)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec())


以上代码展示了如何使用PyQt的信号和槽机制在不同线程之间有效地传递数据,并更新UI,从而避免界面卡死的问题。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: PyQt 在线程中改动组件导致卡死的解决方案