简体   繁体   English

带有 blitting 的 Python、QT 和 matplotlib 散点图

[英]Python, QT and matplotlib scatter plots with blitting

I am trying to animate a scatter plot (it needs to be a scatter plot as I want to vary the circle sizes).我正在尝试为散点图制作动画(它必须是散点图,因为我想改变圆圈大小)。 I have gotten the matplotlib documentation tutorial matplotlib documentation tutorial to work in my PyQT application, but would like to introduce blitting into the equation as my application will likely run on slower machines where the animation may not be as smooth.我已经获得了 matplotlib 文档教程matplotlib 文档教程在我的 PyQT 应用程序中工作,但我想将 blitting 引入等式,因为我的应用程序可能会在动画可能不那么流畅的较慢的机器上运行。

I have had a look at many examples of animations with blitting, but none ever use a scatter plot (they use plot or lines) and so I am really struggling to figure out how to initialise the animation (the bits that don't get re-rendered every time) and the ones that do.我看过很多带有 blitting 的动画示例,但没有一个使用散点图(它们使用绘图或线条),所以我真的很难弄清楚如何初始化动画(那些没有得到重新- 每次都渲染)和那些做的。 I have tried quite a few things, and seem to be getting nowhere (and I am sure they would cause more confusion than help!).我已经尝试了很多东西,但似乎一无所获(我相信它们会引起更多的混乱而不是帮助!)。 I assume that I have missed something fairly fundamental.我认为我错过了一些相当基本的东西。 Has anyone done this before?以前有人这样做过吗? Could anyone help me out splitting the figure into the parts that need to be initiated and the ones that get updates?谁能帮我把图分成需要启动的部分和需要更新的部分?

The code below works, but does not blit.下面的代码有效,但不会 blit。 Appending追加

blit=True

to the end of the animation call yields the following error:到动画调用结束时会产生以下错误:

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

Any help would be great.任何帮助都会很棒。

Regards问候

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

You need to add return self.scat, at the end of the update method if you want to use FuncAnimation with blit=True .如果您想将FuncAnimationblit=True一起使用,则需要在update方法的末尾添加return self.scat, See also this nice StackOverflow post that presents an example of a scatter plot animation with matplotlib using blit.另请参阅这篇不错的StackOverflow 帖子,该帖子展示了使用 blit 使用 matplotlib 制作散点图动画的示例。

As a side-note, if you wish to embed a mpl figure in a Qt application, it is better to avoid using the pyplot interface and to use instead the Object Oriented API of mpl as suggested in the matplotlib documentation .附带说明一下,如果您希望在 Qt 应用程序中嵌入 mpl 图,最好避免使用 pyplot 接口,而是使用matplotlib 文档中建议的 mpl 面向对象的 API。

This could be achieved, for example, as below, where mplWidget can be embedded as any other Qt widget in your main application.这可以实现,例如,如下所示,其中mplWidget可以作为任何其他 Qt 小部件嵌入到您的主应用程序中。 Note that I renamed the update method to update_plot to avoid conflict with the already existing method of the FigureCanvasQTAgg class.请注意,我将update方法重命名为update_plot以避免与FigureCanvasQTAgg类的现有方法发生冲突。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM