matplotlib und PyQt: Dynamische Figur läuft nach mehreren Ladevorgängen langsam oder sieht chaotisch aus

BEARBEITEN:

Ich beschloss, dies umzuschreiben, um ein funktionierendes Beispiel für mein Problem aufzunehmen. Obwohl dies ziemlich lang ist, hoffe ich, dass es sich in Zukunft für viele als nützlich erweist.

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()

Beschreibung des Codes

Ich habe einen GrundQMainWindow mit einer Symbolleiste, die eine Schaltfläche "Anzeigen" hat. Das Hauptfenster erstellt auch eine Zeichenfläche für amatplotlib Abbildung und legt fest, dass es das zentrale Widget ist.

Wenn der Benutzer auf die Schaltfläche "Anzeigen" klickt, werden einige Daten durch Aufrufen der Schaltfläche "Anzeigen" angezeigtdrawGraph() Methode vonGraphCanvas. In einem echten Programm ändern sich diese Daten abhängig davon, was der Benutzer ausgewählt hat, um sie vor dem Klicken auf die Schaltfläche anzuzeigen. Die MethoderesizeEvent() Grundsätzlich wird die Figur erneut gezeichnet, um der neuen Größe des zentralen Widgets Rechnung zu tragen.

DasdrawGraph() Methode erstellt vier Diagramme, von denen das erste Daten enthält, sodass diese sichtbar sind. Die letzten beiden Linien zeichnen die Figur und speichern den statischen Hintergrund der Variablenself.background.

Wenn der Benutzer die Maus auf der Leinwand bewegt, wird zuerst der Hintergrund geladen. Ich wollte den statischen Hintergrund speichern und laden, um das Zeichnen der Figur zu beschleunigen. Danach erhalten die letzten drei Diagramme ihre Daten dynamisch und werden als 3 vertikale Linien angezeigt, die sich mit dem Mauszeiger bewegen.

Das Problem

1) Die Figur wird allmählich langsamer, wenn Sie auf die Schaltfläche "Anzeigen" klicken. Wenn Sie es 50 Mal versuchen und die Maus über die Figur bewegen, sehen Sie, dass die vertikalen Linien viel träger sind. Wenn sich in einer realen Figur viel mehr dynamische Zeichnungen, Anmerkungen usw. befinden, wird das Programm nach nur wenigen Klicks unbrauchbar.

Jemand, der klüger ist, kann wahrscheinlich sagen, warum diese Verlangsamung auftritt, aber ich vermute, dass die zuvor geladenen Figuren irgendwo in der Erinnerung bleiben und möglicherweise unter die neu erstellte Figur gezeichnet werden. Und der Stapel wird immer größer.

2) Die Abbildung wird direkt nach dem Start des Programms angezeigt. Keine große Sache, aber ich würde nur einen leeren Bereich vorziehen, bis der Knopf gedrückt wird.

Eine Lösung ausprobiert

Was ich versucht habe war, dass ich diese beiden Zeilen aus verschoben habedef __init__(self) vonclass MainWindow zudef showButtonClicked(self):

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

So sieht es jetzt aus:

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

Also habe ich die Figur erst erstellt, nachdem der Knopf gedrückt wurde. Dies löst das Problem der Verlangsamung, bringt jedoch ein anderes Problem mit sich. Wenn Sie nun auf die Schaltfläche "Anzeigen" klicken und die Maus auf der Leinwand bewegen, wird der gespeicherte Hintergrund der Figur geladen, jedoch in der Originalgröße von 5 x 4 Zoll, und es kommt zu einem Durcheinander. Der Hintergrund wurde also grundsätzlich nicht in der Größe gespeichert, in der die Figur gezeichnet wurde, sondern in der Größe, in der sie erstellt wurde.

Wenn ich jedoch die Fenstergröße verändere, funktioniert alles gut. Aber wenn ich das nächste Mal auf die Schaltfläche "Anzeigen" klicke, tritt das Problem erneut auf und ich muss die Fenstergröße erneut ändern.

Was ich brauche

Ich muss dafür sorgen, dass das Ding flüssig funktioniert und so aussieht, wie es sollte, egal wie oft auf die Schaltfläche "Anzeigen" geklickt wird. Außerdem wäre es mir lieber, wenn die Abbildung erst dann angezeigt würde, wenn Sie zum ersten Mal auf die Schaltfläche "Anzeigen" klicken, und von diesem Zeitpunkt an bis zum Schließen des Programms sichtbar wäre.

Ein paar Hacks kommen auf mich zu, als würde man die Größe des Fensters um ein Pixel ändern, wenn man auf die Schaltfläche "Anzeigen" klickt, aber das ist nicht der richtige Ansatz, oder?

Alle Ideen und Vorschläge sind mehr als willkommen. Vielen Dank.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage