鱼C论坛

 找回密码
 立即注册
查看: 172|回复: 1

[技术交流] PyQt 在线程中改动组件导致卡死的解决方案

[复制链接]
发表于 2025-3-4 18:53:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
众所周知,使用 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: 操作) 就行了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 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 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-2 02:31

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表