鱼C论坛

 找回密码
 立即注册
查看: 248|回复: 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 的函数

  1. from PyQt6.QtGui import *
  2. from PyQt6.QtCore import *
  3. from PyQt6.QtWidgets import *

  4. class Window(QMainWindow):
  5.     execute = pyqtSignal(name='execute')
  6.     def __init__(self):
  7.         super().__init__(self)
  8.         self.execute.connect(lambda x: x())
复制代码


如上所示,当需要将“线程运行”转换为“不线程运行”时,调用 self.execute.emit(lambda: 操作) 就行了。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-3-4 18:54:03 | 显示全部楼层
解决在PyQt中线程对于UI组件改动导致卡死的问题,您已经提到了使用 pyqtSignal 和多线程的结合。以下是通过使用PyQt的信号-槽机制来解决这个问题的详细步骤:

步骤 1:定义信号

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


  1. class Window(QMainWindow):
  2.     update_ui_signal = pyqtSignal(str)
复制代码


步骤 2:连接信号与槽

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


  1. class Window(QMainWindow):
  2.     def __init__(self):
  3.         super().__init__()
  4.         self.update_ui_signal.connect(self.update_ui)

  5.     def update_ui(self, message):
  6.         # 这里处理UI更新,例如更新QTextBrowser的内容
  7.         self.text_browser.append(message)
复制代码


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

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


  1. def run_in_thread(self):
  2.     # 模拟从socket接收数据
  3.     while True:
  4.         data = receive_data_from_socket()
  5.         # 使用 信号 来发出数据进行UI更新
  6.         self.update_ui_signal.emit(data)
复制代码


完整示例

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


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

  4. class Worker(QThread):
  5.     data_received = pyqtSignal(str)

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

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

  15. class Window(QMainWindow):
  16.     def __init__(self):
  17.         super().__init__()
  18.         
  19.         self.text_browser = QTextBrowser(self)
  20.         self.setCentralWidget(self.text_browser)

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

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

  27. if __name__ == "__main__":
  28.     app = QApplication(sys.argv)
  29.     window = Window()
  30.     window.show()
  31.     sys.exit(app.exec())
复制代码


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

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-23 03:47

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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