PyQt4、QThread 和打开大文件而不冻结 GUI

发布于 2024-08-12 17:59:48 字数 634 浏览 2 评论 0原文

我想问如何从磁盘读取大文件并保持 PyQt4 UI 响应(不阻塞)。我已将文件的负载移至 QThread 子类,但我的 GUI 线程被冻结。有什么建议吗?我想这一定是GIL的问题,但我不知道如何排序?

编辑: 我正在使用 GDCM 项目中的 vtkGDCMImageReader 来读取多帧 DICOM 图像并使用 vtk 和 pyqt4 显示它。我在不同的线程(QThread)中执行此加载,但我的应用程序冻结直到加载图像。这是一个示例代码:

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
        self.reader.vtkgdcm.vtkGDCMImageReader()

    def run(self): 
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

I would like to ask how to read a big file from disk and maintain the PyQt4 UI responsive (not blocked). I had moved the load of the file to a QThread subclass but my GUI thread get freezed. Any suggestions? I think it must be something with the GIL but I don't know how to sort it?

EDIT:
I am using vtkGDCMImageReader from the GDCM project to read a multiframe DICOM image and display it with vtk and pyqt4. I do this load in a different thread (QThread) but my app freeze until the image is loaded. here is an example code:

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
        self.reader.vtkgdcm.vtkGDCMImageReader()

    def run(self): 
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

深海里的那抹蓝 2024-08-19 17:59:48

我猜您是直接调用 run 来开始线程。这会使 GUI 冻结,因为您没有激活该线程。

因此,您会错过那里的 start ,它将间接且正确地调用 run

thread = ReadThread()
thread.begin()

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
        self.reader.vtkgdcm.vtkGDCMImageReader()

    def run(self): 
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

    def begin(self): 
        self.start()

I'm guessing you're directly calling the run to begin the thread. That would make the GUI freeze because you're not activating the thread.

So you'd be missing the start there, which would indirectly and properly call the run:

thread = ReadThread()
thread.begin()

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
        self.reader.vtkgdcm.vtkGDCMImageReader()

    def run(self): 
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

    def begin(self): 
        self.start()
娇俏 2024-08-19 17:59:48

有点晚了,但我想我可以查明您面临的问题。

图像很大,解包可能是 CPU 密集型任务。这意味着您的 GUI 线程进入睡眠状态,并且加载线程受 CPU 限制。此时加载线程具有 GIL,并且 GUI 无法启动。

即使您可以进入加载线程,并引入 sleep(0),以允许 GUI 继续,这对多核或多处理器计算机也没有帮助。发生的情况是操作系统有两个线程并且认为它可以运行这两个线程。加载线程设置在(例如)核心 1 上,GUI 可以在(例如)核心 2 上加载和运行。因此,在核心 2 上启动 GUI 线程的加载和启动后,操作系统将恢复加载线程在核心 1 上 - 立即获取 GIL。片刻之后,GUI 线程准备启动,并尝试获取 GIL,但失败了。如果没有 GIL,它所能做的就是回去睡觉!

一种解决方案是按策略间隔在后台线程中插入短暂的(大于零)睡眠,以便 GUI 可以运行。这并不总是可能的。

A bit late, but I think I can pinpoint the problem you face.

The image is large, and the unpacking is probably a CPU intensive task. This means that your GUI thread goes to sleep and the loading thread is CPU bound. At that point the loading thread has the GIL, and the GUI cannot start.

Even if you can get into the loading thread, and introduce a sleep(0), to alow the GUI to continue, this will not help on a mulit-core or multi-processor machine. What happens is the O/S has two threads and thinks it can run both. The loading thread is set up on (say) core 1 and the GUI can be loaded and run on (say) core 2. So after initiating the load and start of the GUI thread on core 2, the O/S resumes the loading thread on core 1 - which promtply grabs the GIL. Moments later the GUI thread is ready to start, and attempts to aquire the GIL, which fails. All it can do without the GIL is go back to sleep!

One solution is to insert a short (greater than zero) sleep in the background thread at strategic intervals, so that the GUI can run. This is not always possible.

彡翼 2024-08-19 17:59:48

也许在线程中创建你的阅读器对象:

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
-       self.reader = vtkgdcm.vtkGDCMImageReader()

    def run(self): 
+       self.reader = vtkgdcm.vtkGDCMImageReader()
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

Maybe create your reader object in the tread:

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
-       self.reader = vtkgdcm.vtkGDCMImageReader()

    def run(self): 
+       self.reader = vtkgdcm.vtkGDCMImageReader()
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文