matplotlib i PyQt: Dynamiczna figura działa powoli po kilku obciążeniach lub wygląda na nieuporządkowaną

EDYTOWAĆ:

Postanowiłem przepisać to, dodając roboczy przykład mojego problemu. Chociaż jest to dość długi, mam nadzieję, że okaże się przydatny dla wielu w przyszłości.

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

Opis kodu

Mam podstawowyQMainWindow z paskiem narzędzi, który ma przycisk „pokaż”. Główne okno tworzy także kanwę dlamatplotlib rysunek i ustawia go jako centralny widget.

Gdy użytkownik kliknie przycisk „pokaż”, niektóre dane są wyświetlane przez wywołaniedrawGraph() metodaGraphCanvas. W prawdziwym programie dane zmieniają się w zależności od tego, co użytkownik wybrał do wyświetlenia przed kliknięciem przycisku. MetodaresizeEvent() zasadniczo rysuje figurę ponownie, aby pomieścić nowy rozmiar centralnego widgetu.

ThedrawGraph() metoda tworzy cztery wykresy, z których pierwszy ma dane, więc jest widoczny. Ostatnie dwie linie rysują figurę i zapisują statyczne tło do zmiennejself.background.

Gdy użytkownik przesuwa mysz na płótno, tło jest ładowane jako pierwsze. Chciałem zapisać i załadować statyczne tło, aby rysunek rysował się szybciej. Następnie trzy ostatnie wykresy pobierają swoje dane dynamicznie i są wyświetlane jako 3 pionowe linie, które poruszają się wraz z kursorem myszy.

Problem

1) Postać staje się stopniowo wolniejsza, gdy klikniesz przycisk „pokaż”. Jeśli spróbujesz uderzyć go jak 50 razy i przesunąć mysz na figurkę, zobaczysz, że pionowe linie są znacznie bardziej opóźnione. Gdy jest o wiele więcej dynamicznych wykresów i adnotacji itp. W prawdziwej postaci, program staje się bezużyteczny po kilku kliknięciach.

Ktoś mądrzejszy może prawdopodobnie powiedzieć, dlaczego dzieje się to spowolnienie, ale przypuszczam, że wcześniej załadowane postacie są przechowywane gdzieś w pamięci i być może są rysowane pod nowo utworzoną figurą. A stos ciągle się powiększa.

2) Rysunek jest pokazany zaraz po uruchomieniu programu. Nie jest to wielka sprawa, ale wolałbym tylko pusty obszar, dopóki przycisk nie zostanie kliknięty.

Próbowano rozwiązania

Próbowałem przenieść te dwie liniedef __init__(self) zclass MainWindow dodef showButtonClicked(self):

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

Wygląda teraz tak:

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

Więc stworzyłem figurę dopiero po naciśnięciu przycisku. To rozwiązuje problem spowolnienia, ale powoduje inny problem. Teraz, gdy naciśniesz przycisk „pokaż” i przesuniesz mysz na płótnie, zapisane tło tła zostanie załadowane, ale w oryginalnym rozmiarze 5 na 4 cale i mamy bałagan. Zasadniczo tło zostało zapisane nie w rozmiarze, w jakim figurka została narysowana, ale w rozmiarze, w jakim została utworzona.

Jeśli jednak zmienię rozmiar okna, wszystko działa dobrze. Ale następnym razem, gdy kliknę przycisk „pokaż”, problem pojawi się ponownie i muszę ponownie zmienić rozmiar okna.

Czego potrzebuję

Muszę sprawić, aby ta rzecz działała płynnie i wyglądała tak, jak gdyby nie miało znaczenia, ile razy przycisk „pokaż” zostanie kliknięty. Ponadto wolałbym, aby postać nie była wyświetlana, dopóki przycisk „pokaż” nie zostanie kliknięty po raz pierwszy i od tego momentu będzie widoczny, dopóki program nie zostanie zamknięty.

Kilka hacków przychodzi mi do głowy, jak zmiana rozmiaru okna o jeden piksel po kliknięciu przycisku „show”, ale to nie jest właściwe podejście, prawda?

Wszelkie pomysły i sugestie są mile widziane. Dziękuję Ci.

questionAnswers(1)

yourAnswerToTheQuestion