How do you run Matplotlib in anothor thread on PyQt
I need to display some charts in my PyQt application, so I write down these code. It is works, but sometimes, draw a chart will spend a lot of time, it will "freeze" the main window.
I think do it in another thread should resolved it. But how can I do it? Or, there is another way to draw a chart in "non-block" mode?
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...
The basics of Python threading for simple uses like this are really easy.
You'll need to
and then, instead of just calling x.draw(), create a thread and run it as follows:
draw_thread = threading.Thread(target=x.draw) draw_thread.start()
That will probably get you 90% of the way at least. You can read the docs to detect things like still-running threads, waiting for threads, and so on. But at heart, it's not difficult and if ti's just simple interactive graphics this might even suffice.
Be aware, though, that if draw() references globals, there is the potential for race conditions to occur,making your program unreliable. This is a further advantage of writing code with good coupling, clean interfaces and no references to mutable globals (unless locking is used to protect those resources).
You should us QThreads in your Qt application (to make the frame work do all of the hard work for you)
put your time consuming work in a worker class (that is derived from QObject)
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()
and communicate with your worker via signals/slots. If you call it directly (as in self.worker.do_slow_stuff()), it will run on the thread you call it from, which will block the main event loop making the interface freeze.