matplotlib y PyQt: la figura dinámica se ejecuta lentamente después de varias cargas o parece desordenada

EDITAR:

Decidí volver a escribir esto para incluir un ejemplo funcional de mi problema. Aunque esto es bastante largo, espero que sea útil para muchos en el futuro.

import sys

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

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.setGeometry(100, 100, 640, 480)
        showButton = QPushButton('Show')

        toolbarShowButton = self.addToolBar('Show button toolbar')

        toolbarShowButton.addWidget(showButton)

        self.connect(showButton, SIGNAL('clicked()'), self.showButtonClicked)
        self.graphLabel = GraphCanvas(self);
        self.setCentralWidget(self.graphLabel)

    def showButtonClicked(self):  
        self.graphLabel.drawGraph()

    def resizeEvent(self, event):
        try:
            self.graphLabel.setFig()
        except AttributeError:
            pass

class GraphCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):

        self.fig = Figure(figsize=(width, height), dpi=dpi)

        self.axes = self.fig.add_subplot(111)

        FigureCanvas.__init__(self, self.fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        self.background = None

    def drawGraph(self):
        self.axes.cla()
        self.someplot = self.axes.plot(range(1,5), range(1,5))
        self.redVert, = self.axes.plot(None, None, 'r--')
        self.greenVert, = self.axes.plot(None, None, 'g--')
        self.yellowVert, = self.axes.plot(None, None, 'y--')

        self.verticalLines = (self.redVert, self.greenVert, self.yellowVert)

        self.fig.canvas.mpl_connect('motion_notify_event', self.onMove)

        self.draw()
        self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)

    def onMove(self, event):

        # cursor moves on the canvas
        if event.inaxes:

            # restore the clean background
            self.fig.canvas.restore_region(self.background)
            ymin, ymax = self.axes.get_ylim()
            x = event.xdata - 1

            # draw each vertical line
            for line in self.verticalLines:
                line.set_xdata((x,))
                line.set_ydata((ymin, ymax))
                self.axes.draw_artist(line)
                x += 1

            self.fig.canvas.blit(self.axes.bbox)

    def setFig(self):
        '''
        Draws the canvas again after the main window
        has been resized.
        '''

        try:
            # hide all vertical lines
            for line in self.verticalLines:
                line.set_visible(False)

        except AttributeError:
            pass

        else:
            # draw canvas again and capture the background
            self.draw()
            self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)

            # set all vertical lines visible again
            for line in self.verticalLines:
                line.set_visible(True)

def main():
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())

if __name__ == '__main__': main()

Descripción del código

Tengo un basicoQMainWindow con una barra de herramientas que tiene un botón "mostrar". La ventana principal también crea un lienzo para unmatplotlib Figura y lo configura como el widget central.

Cuando el usuario pulsa el botón "mostrar", se muestran algunos datos llamando aldrawGraph() método deGraphCanvas. En un programa real, los datos cambian según lo que el usuario haya seleccionado para mostrar antes de hacer clic en el botón. El métodoresizeEvent() Básicamente, vuelve a dibujar la figura para acomodar el nuevo tamaño del widget central.

losdrawGraph() El método crea cuatro gráficos de los cuales el primero tiene datos, por lo que es visible. Las dos últimas líneas dibujan la figura y guardan el fondo estático en la variableself.background.

Cuando el usuario mueve el mouse en el lienzo, el fondo se carga primero. Quería guardar y cargar el fondo estático para que la figura se dibuje más rápido. Después de eso, los últimos tres gráficos obtienen sus datos dinámicamente y se muestran como 3 líneas verticales que se mueven con el cursor del mouse.

El problema

1) La figura se vuelve gradualmente más lenta a medida que haces clic en el botón "mostrar". Si intentas golpearlo 50 veces y mueves el mouse sobre la figura, verás que las líneas verticales son mucho más lentas. Cuando hay gráficos y anotaciones mucho más dinámicos en una figura real, el programa se vuelve inutilizable después de unos pocos clics.

Alguien más sabio probablemente puede decir por qué está ocurriendo esta desaceleración, pero supongo que las figuras cargadas previamente se guardan en algún lugar de la memoria y quizás se dibujan debajo de la figura recién creada. Y la pila sigue creciendo y creciendo.

2) La figura se muestra justo después de iniciar el programa. No es un gran problema, pero preferiría un área en blanco hasta que se haga clic en el botón.

Una solucion probada

Lo que intenté fue que moví estas dos líneas dedef __init__(self) declass MainWindow adef showButtonClicked(self):

self.graphLabel = GraphCanvas(self);
self.setCentralWidget(self.graphLabel)

Así que ahora se ve así:

def showButtonClicked(self):  
    self.graphLabel = GraphCanvas(self);
    self.setCentralWidget(self.graphLabel)
    self.graphLabel.drawGraph()

Así que creé la figura solo después de presionar el botón. Eso resuelve el problema de la desaceleración, pero trae otro problema. Ahora, cuando presionas el botón "mostrar" y mueves el mouse sobre el lienzo, el fondo guardado de la figura se carga, pero en el tamaño original de 5 por 4 pulgadas y tenemos un desastre. Así que, básicamente, el fondo no se guardó en el tamaño en que se dibujó la figura, sino en el tamaño en que se creó.

Sin embargo, si cambio el tamaño de la ventana, todo funciona bien. Pero la próxima vez que haga clic en el botón "mostrar", el problema vuelve a aparecer y debo cambiar el tamaño de la ventana nuevamente.

Lo que necesito

Necesito hacer que esto funcione de manera fluida y que se vea como debería, sin importar cuántas veces se haga clic en el botón "Mostrar". Además, preferiría que la figura no se mostrara hasta que se haga clic por primera vez en el botón "mostrar" y que desde ese momento sea visible hasta que se cierre el programa.

Cuando se hace clic en el botón "Mostrar", aparece un par de hacks, como cambiar el tamaño de la ventana un píxel, pero ese no es el enfoque correcto, ¿verdad?

Cualquier idea y sugerencia son más que bienvenidas. Gracias.

Respuestas a la pregunta(1)

Su respuesta a la pregunta