2016-10-03 3 views
2

Ich versuche, ein Streudiagramm zu animieren (es muss ein Streudiagramm sein, da ich die Kreisgrößen variieren möchte). Ich habe das matplotlib-Dokumentationstutorial matplotlib documentation tutorial in meiner PyQT-Anwendung erhalten, möchte aber Blitting in die Gleichung einfügen, da meine Anwendung wahrscheinlich auf langsameren Maschinen läuft, auf denen die Animation möglicherweise nicht so flüssig ist.Python, QT und Matplotlib Streudiagramme mit Blitting

Ich habe mir viele Beispiele von Animationen mit Blitting angeschaut, aber keines benutzt jemals ein Streudiagramm (sie benutzen Plot oder Linien) und so habe ich wirklich Mühe herauszufinden, wie man die Animation initialisiert (die Bits, die don nicht jedes Mal neu gerendert) und diejenigen, die das tun. Ich habe einige Dinge ausprobiert und scheint nirgendwohin zu kommen (und ich bin mir sicher, dass sie mehr Verwirrung als Hilfe verursachen würden!). Ich nehme an, dass ich etwas ziemlich Grundlegendes verpasst habe. Hat jemand das schon mal gemacht? Könnte mir jemand helfen, die Figur in die Teile zu teilen, die initiiert werden müssen, und diejenigen, die Updates erhalten?

Der folgende Code funktioniert, aber nicht blit. Anfügen

blit=True 

bis zum Ende der Animation Aufruf ergibt die folgende Fehlermeldung:

RuntimeError: The animation function must return a sequence of Artist objects. 

Jede Hilfe wäre toll.

Grüße

FP

import numpy as np 
from PyQt4 import QtGui, uic 
import sys 
import matplotlib.pyplot as plt 
from matplotlib.animation import FuncAnimation 

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

     self.setupAnim() 

     self.show() 

    def setupAnim(self): 
     self.fig = plt.figure(figsize=(7, 7)) 
     self.ax = self.fig.add_axes([0, 0, 1, 1], frameon=False) 
     self.ax.set_xlim(0, 1), self.ax.set_xticks([]) 
     self.ax.set_ylim(0, 1), self.ax.set_yticks([]) 

     # Create rain data 
     self.n_drops = 50 
     self.rain_drops = np.zeros(self.n_drops, dtype=[('position', float, 2), 
               ('size',  float, 1), 
               ('growth', float, 1), 
               ('color', float, 4)]) 

     # Initialize the raindrops in random positions and with 
     # random growth rates. 
     self.rain_drops['position'] = np.random.uniform(0, 1, (self.n_drops, 2)) 
     self.rain_drops['growth'] = np.random.uniform(50, 200, self.n_drops) 

     # Construct the scatter which we will update during animation 
     # as the raindrops develop. 
     self.scat = self.ax.scatter(self.rain_drops['position'][:, 0], self.rain_drops['position'][:, 1], 
          s=self.rain_drops['size'], lw=0.5, edgecolors=self.rain_drops['color'], 
          facecolors='none') 

     self.animation = FuncAnimation(self.fig, self.update, interval=10) 
     plt.show() 

    def update(self, frame_number): 
     # Get an index which we can use to re-spawn the oldest raindrop. 
     self.current_index = frame_number % self.n_drops 

     # Make all colors more transparent as time progresses. 
     self.rain_drops['color'][:, 3] -= 1.0/len(self.rain_drops) 
     self.rain_drops['color'][:, 3] = np.clip(self.rain_drops['color'][:, 3], 0, 1) 

     # Make all circles bigger. 
     self.rain_drops['size'] += self.rain_drops['growth'] 

     # Pick a new position for oldest rain drop, resetting its size, 
     # color and growth factor. 
     self.rain_drops['position'][self.current_index] = np.random.uniform(0, 1, 2) 
     self.rain_drops['size'][self.current_index] = 5 
     self.rain_drops['color'][self.current_index] = (0, 0, 0, 1) 
     self.rain_drops['growth'][self.current_index] = np.random.uniform(50, 200) 

     # Update the scatter collection, with the new colors, sizes and positions. 
     self.scat.set_edgecolors(self.rain_drops['color']) 
     self.scat.set_sizes(self.rain_drops['size']) 
     self.scat.set_offsets(self.rain_drops['position']) 

if __name__== '__main__': 
    app = QtGui.QApplication(sys.argv) 
    window = MainWindow() 
    sys.exit(app.exec_()) 

Antwort

1

Sie müssen return self.scat, am Ende des update Methode hinzufügen, wenn Sie FuncAnimation mit blit=True verwenden möchten. Siehe auch dieses nette StackOverflow post, das ein Beispiel für eine Streudiagramm-Animation mit Matplotlib unter Verwendung von Blit darstellt. Wenn Sie eine MPL-Figur in eine Qt-Anwendung einbetten möchten, ist es besser, die pyplot-Schnittstelle zu verwenden und statt dessen die objektorientierte API von mpl zu verwenden, wie in der matplotlib documentation vorgeschlagen.

Dies könnte zum Beispiel wie folgt erreicht werden, wobei mplWidget wie jedes andere Qt-Widget in Ihrer Hauptanwendung eingebettet werden kann. Beachten Sie, dass ich die update-Methode in update_plot umbenannt habe, um einen Konflikt mit der bereits vorhandenen Methode der Klasse FigureCanvasQTAgg zu vermeiden.

import numpy as np 
from PyQt4 import QtGui 
import sys 
import matplotlib as mpl 
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg 
from matplotlib.animation import FuncAnimation 
import matplotlib.pyplot as plt 

class mplWidget(FigureCanvasQTAgg): 
    def __init__(self): 
     super(mplWidget, self).__init__(mpl.figure.Figure(figsize=(7, 7))) 

     self.setupAnim() 
     self.show() 

    def setupAnim(self): 
     ax = self.figure.add_axes([0, 0, 1, 1], frameon=False) 
     ax.axis([0, 1, 0, 1]) 
     ax.axis('off') 

     # Create rain data 
     self.n_drops = 50 
     self.rain_drops = np.zeros(self.n_drops, dtype=[('position', float, 2), 
                 ('size',  float, 1), 
                 ('growth', float, 1), 
                 ('color', float, 4) 
                 ]) 

     # Initialize the raindrops in random positions and with 
     # random growth rates. 
     self.rain_drops['position'] = np.random.uniform(0, 1, (self.n_drops, 2)) 
     self.rain_drops['growth'] = np.random.uniform(50, 200, self.n_drops) 

     # Construct the scatter which we will update during animation 
     # as the raindrops develop. 
     self.scat = ax.scatter(self.rain_drops['position'][:, 0], 
           self.rain_drops['position'][:, 1], 
           s=self.rain_drops['size'], 
           lw=0.5, facecolors='none', 
           edgecolors=self.rain_drops['color']) 

     self.animation = FuncAnimation(self.figure, self.update_plot, 
             interval=10, blit=True) 

    def update_plot(self, frame_number): 
     # Get an index which we can use to re-spawn the oldest raindrop. 
     indx = frame_number % self.n_drops 

     # Make all colors more transparent as time progresses. 
     self.rain_drops['color'][:, 3] -= 1./len(self.rain_drops) 
     self.rain_drops['color'][:, 3] = np.clip(self.rain_drops['color'][:, 3], 0, 1) 

     # Make all circles bigger. 
     self.rain_drops['size'] += self.rain_drops['growth'] 

     # Pick a new position for oldest rain drop, resetting its size, 
     # color and growth factor. 
     self.rain_drops['position'][indx] = np.random.uniform(0, 1, 2) 
     self.rain_drops['size'][indx] = 5 
     self.rain_drops['color'][indx] = (0, 0, 0, 1) 
     self.rain_drops['growth'][indx] = np.random.uniform(50, 200) 

     # Update the scatter collection, with the new colors, 
     # sizes and positions. 
     self.scat.set_edgecolors(self.rain_drops['color']) 
     self.scat.set_sizes(self.rain_drops['size']) 
     self.scat.set_offsets(self.rain_drops['position']) 

     return self.scat, 


if __name__ == '__main__': 
    app = QtGui.QApplication(sys.argv) 
    window = mplWidget() 
    sys.exit(app.exec_()) 
+0

Das ist großartig. Danke Jean, sehr geschätzt. Interessant, die Kollision mit der "update" -Methode zu beachten. Wenn ich darüber nachdachte, war es wahrscheinlich das, was mich davon abhielt, es ein paar Mal zum Laufen zu bringen. Do! Hinweis an mich selbst: Verwenden Sie keine allgemeinen Begriffe für Funktionsnamen! – fp1991