【问题标题】:Clearing background in matplotlib using wxPython使用 wxPython 清除 matplotlib 中的背景
【发布时间】:2011-05-12 11:06:02
【问题描述】:

我想用 matplotlib 创建一个动画来监控聚类算法的收敛性。它应该在第一次调用时绘制我的数据的散点图,并在每次更新绘图时绘制错误椭圆。我正在尝试使用canvas_copy_from_bbox()restore_region() 来保存散点图,然后在更新绘图时绘制一组新的椭圆。 但是,代码只是在旧椭圆之上绘制了新的椭圆,而没有先清除之前的图。 我怀疑,不知何故,这种方法不适用于Ellipse()add_path() 命令,但我不知道如何解决这个问题。

代码如下:

import wx
import math 
from math import pi
from matplotlib.patches import Ellipse
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
     FigureCanvasWxAgg as FigureCanvas

TIMER_ID = wx.NewId()


class _MonitorPlot(wx.Frame):
    def __init__(self, data, scale=1):
        self.scale = scale
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          title="FlowVB Progress Monitor", size=(800, 600))
        self.fig = Figure((8, 6), 100)
        self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig)
        self.ax = self.fig.add_subplot(111)

        x_lims = [data[:, 0].min(), data[:, 0].max()]
        y_lims = [data[:, 1].min(), data[:, 1].max()]

        self.ax.set_xlim(x_lims)
        self.ax.set_ylim(y_lims)
        self.ax.set_autoscale_on(False)

        self.l_data = self.ax.plot(data[:, 0], data[:, 1], color='blue',
                               linestyle='', marker='o')

        self.canvas.draw()
        self.bg = self.canvas.copy_from_bbox(self.ax.bbox)

        self.Bind(wx.EVT_IDLE, self._onIdle)

    def update_plot(self, pos, cov):
        self.canvas.restore_region(self.bg)

        for k in range(pos.shape[0]):
            l_center, = self.ax.plot(pos[k, 0], pos[k, 1],
                                     color='red', marker='+')

            U, s, Vh = np.linalg.svd(cov[k, :, :])
            orient = math.atan2(U[1, 0], U[0, 0]) * 180 / pi
            ellipsePlot = Ellipse(xy=pos[k, :], width=2.0 * math.sqrt(s[0]),
                                  height=2.0 * math.sqrt(s[1]),
                                  angle=orient, facecolor='none',
                                  edgecolor='red')
            self.ax.add_patch(ellipsePlot)

        self.canvas.draw()
        self.canvas.blit(self.ax.bbox)

【问题讨论】:

    标签: python wxpython matplotlib


    【解决方案1】:

    发生的情况是,您每次都在向绘图中添加新补丁,然后在调用 self.canvas.draw() 时绘制所有补丁。

    最快的解决方法是在添加每个补丁后调用self.canvas.draw_artist(ellipsePlot),然后删除对self.canvas.draw()的调用

    作为一个简单的独立示例:

    # Animates 3 ellipses overlain on a scatterplot
    import matplotlib.pyplot as plt
    from matplotlib.patches import Ellipse
    import numpy as np
    
    num = 10
    x = np.random.random(num)
    y = np.random.random(num)
    
    plt.ion()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    line = ax.plot(x, y, 'bo')
    
    fig.canvas.draw()
    bg = fig.canvas.copy_from_bbox(ax.bbox)
    
    # Pseudo-main loop
    for i in range(100):
        fig.canvas.restore_region(bg)
    
        # Make a new ellipse each time... (inefficient!)
        for i in range(3):
            width, height, angle = np.random.random(3)
            angle *= 180
            ellip = Ellipse(xy=(0.5, 0.5), width=width, height=height, 
                    facecolor='red', angle=angle, alpha=0.5)
            ax.add_patch(ellip)
            ax.draw_artist(ellip)
    
        fig.canvas.blit(ax.bbox)
    

    但是,随着时间的推移,这可能会导致内存消耗问题,因为轴对象将跟踪添加到其中的所有艺术家。如果您的斧头长时间不挂机,这可能可以忽略不计,但您至少应该意识到它会导致内存泄漏。解决此问题的一种方法是通过在绘制后为每个椭圆调用ax.remove(ellipsePlot) 来从轴上移除省略号艺术家。但是,这仍然有点低效,因为您不断地创建和销毁椭圆艺术家,而您可以更新它们。 (创建和销毁它们根本没有太多开销,不过,这主要是一个风格问题......)

    如果椭圆的数量随着时间的推移保持不变,那么只更新每个椭圆艺术家对象的属性而不是创建和添加新对象会更好也更容易。这将避免从轴上删除“旧”椭圆,因为只有您需要的数字才会存在。

    作为一个简单的独立示例:

    # Animates 3 ellipses overlain on a scatterplot
    import matplotlib.pyplot as plt
    from matplotlib.patches import Ellipse
    import numpy as np
    
    num = 10
    x = np.random.random(num)
    y = np.random.random(num)
    
    plt.ion()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    line = ax.plot(x, y, 'bo')
    
    fig.canvas.draw()
    bg = fig.canvas.copy_from_bbox(ax.bbox)
    
    # Make and add the ellipses the first time (won't ever be drawn)
    ellipses = []
    for i in range(3):
        ellip = Ellipse(xy=(0.5, 0.5), width=1, height=1, 
                facecolor='red', alpha=0.5)
        ax.add_patch(ellip)
        ellipses.append(ellip)
    
    # Pseudo-main loop
    for i in range(100):
        fig.canvas.restore_region(bg)
    
        # Update the ellipse artists...
        for ellip in ellipses:
            ellip.width, ellip.height, ellip.angle = np.random.random(3)
            ellip.angle *= 180
            ax.draw_artist(ellip)
    
        fig.canvas.blit(ax.bbox)
    

    【讨论】:

    • 除了fig.canvas.blit(ax.bbox),还可以考虑fig.canvas.update()。根据this blog,它同样快。对我来说,它解决了内存溢出问题。
    • @LucM - 对于它的价值,这在很大程度上取决于后端。 fig.canvas.blit(updated_region) 将更快,并且与TkAgg 和其他一些常见后端完美配合。但是,在其他后端,fig.canvas.update()(刷新整个画布)更快,因为不完全支持对子区域进行位块传输。无论如何,了解两者都很好。
    • 如果我切换后端,这将非常有用。使用 PyQt 时出现 blit 内存溢出。
    猜你喜欢
    • 1970-01-01
    • 2015-05-08
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多