在PyQt中如何在另一个线程中运行Matplotlib?

3

我需要在我的PyQt应用程序中显示一些图表,所以我编写了这些代码。虽然它可以工作,但有时候绘制一个图表会花费很长时间,这会导致主窗口“冻结”。

我认为在另一个线程中执行可以解决这个问题。但是我应该如何做呢?或者,有没有其他方法可以以“非阻塞”方式绘制图表?

from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas   
from matplotlib.figure import Figure

class MplCanvas(FigureCanvas):

    def __init__(self):
        self.fig = Figure()
        self.axes = self.fig.add_subplot(111)

        # do something...
        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        # do something...

    def draw(self):
        # do something...

我正在实现类似的东西。这是我的想法。你需要使用多个类,其中一个基于PyQt的QDialog或类似的类用于GUI输出。另一个类是基于QThread的工作类。一旦QThread完成绘图,它将使用PyQt的信号引擎将图形和画布实例传递给GUI对象。我尝试将QDialog和QThread合并为一个类,但显然不能这样做。(我相信你可以这样做,但使用两个单独的类更容易) - Barmaley
2个回答

2
你应该在你的Qt应用程序中使用QThreads(让框架为你完成所有繁重的工作)。
  1. put your time consuming work in a worker class (that is derived from QObject)

  2. In you class that is caling the plotting add something like

    self.thread = QtCore.QThread(parent=self)
    
    self.worker = Worker(parent=None)
    self.worker.moveToThread(self.thread)
    self.thread.start()
    
通过信号/槽与您的工作程序通信。如果直接调用它(例如 `self.worker.do_slow_stuff()`),它将在调用它的线程上运行,这将阻塞主事件循环使界面冻结。
一个关于如何使用 `QThread` 进行线程处理的好 解释(同时也要看不要做什么,首先现在描述默认行为)。

只是出于好奇问一个问题。您是否曾成功地在Windows上让QThread应用程序长时间稳定运行而没有崩溃?它让我发疯了,最终我放弃了,并改变了整个程序模型以使用多进程。 - The Quantum Physicist

1
Python线程的基础知识对于像这样简单的用途来说非常容易。

您需要:

import threading

然后,不仅仅是调用x.draw(),创建一个线程并按如下方式运行:

draw_thread = threading.Thread(target=x.draw)
draw_thread.start()

那至少可以让你达到90%的效果。您可以阅读文档以检测诸如仍在运行的线程、等待线程等情况。但本质上,这并不困难,如果只是简单的交互式图形,甚至可能足够。 请注意,如果draw()引用全局变量,则存在发生竞态条件的可能,从而使您的程序不可靠。这是使用良好耦合、清晰界面和没有对可变全局变量的引用(除非使用锁来保护这些资源)编写代码的另一个优点。

你必须在PyQt中使用QThreads。普通的Thread将无法正常工作。 - Barmaley
啊,这对我来说是个新闻 - 感谢您的评论。 QThreadsthreading 线程能和平共处吗? - holdenweb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接