views:

233

answers:

0

I have a thread that updates some data periodically. I want do visualize the data with matplotlib. When the thread updates the data, the plot should be updated as well. I tried embedding a matplotlib.FigureCanvas(see following snippet) in a QWidget...

class MplSubPlotCanvas(FigureCanvas):
     def __init__(self, parent=None):
        self.fig = Figure(figsize = (10, 10), dpi=60)
        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self,
                QSizePolicy.Expanding,
                QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        self.fig.add_subplot(1,1,1)

    def sizeHint(self):
         w, h = self.get_width_height()
        return QSize(w, h)

    def minimumSizeHint(self):
        return QSize(100, 100)

class MplSubPlotWidget(QWidget):
    def __init__(self, parent = None):
        QWidget.__init__(self, parent)
        self.canvas = MplSubPlotCanvas()
        self.hbox = QHBoxLayout()
        self.hbox.addWidget(self.canvas)
        self.setLayout(self.hbox)

    def paintEvent(self, event):
        self.canvas.draw()

However, when I draw into the figure of that cavas from another thread than the GUI thread I always get an error message saying that I can't draw from outside the GUI thread. My solution so far is: 1. when the data is updated, emit a signal 'paint()' 2. the signal is caught by a slot paint() that does the drawing and calls repaint of the FigureCanvas shown above (this should happen in the main GUI thread and so drawing is actually done in the main GUI thread)...

class PainterWidget(MplSubPlotWidget, Observer):
    def __init__(self, parent=None):
        MplSubPlotWidget.__init__(self, parent=parent)
        Observer.__init__(self)
        QtCore.QObject.connect(self, QtCore.SIGNAL('paint()'), self.paint)

    def dataChanged(self):
        # since we can only paint in the main GUI thread, we need to cue an
        # event wich will be caught by the main thread, instead of painting
        # directly
        self.emit(QtCore.SIGNAL('paint()'))

    def paint(self):
        # paint something here into the figure of the canvas
        self.repaint()

After the data has changed dataChanged() is called, which emits the paint signal. The signal is handled by paint(), where plot is being updated.

A wonder if there is a better approach to embed a plot in a QWidget and update in from another thread.