【问题标题】:Python Matplotlib interactive plotting - freezes after a few framesPython Matplotlib 交互式绘图 - 几帧后冻结
【发布时间】:2012-11-12 18:08:49
【问题描述】:

我在让 matplotlib 与交互式绘图一起正常工作时遇到问题……我看到的是,在显示几帧我的模拟数据后,matplotlib 挂起 - 并且不再显示。

基本上我一直在玩一些科学模拟 - 并且希望能够在制作结果时绘制我的结果 - 而不是在最后 - 使用 pylab.show()。

我从不久前发现了一个食谱示例,它似乎可以满足我的需求 - 简单来说(尽管数据不同)。食谱在这里...http://www.scipy.org/Cookbook/Matplotlib/Animations#head-2f6224cc0c133b6e35c95f4b74b1b6fc7d3edca4

我搜索了一下,我知道有些人以前遇到过这些问题 - Matplotlib animation either freezes after a few frames or just doesn't work 但当时似乎没有好的解决方案。我想知道是否有人在这里找到了一个好的解决方案。

我在 matplotlib 上尝试了一些“后端”......TkAgg 似乎适用于几个帧...... qt4agg 不显示帧。我还没有正确安装 GTK。

我正在运行最新的 pythonxy(2.7.3)。

有人有什么建议吗?

import matplotlib
matplotlib.use('TkAgg') # 'Normal' Interactive backend. - works for several frames
#matplotlib.use('qt4agg') # 'QT' Interactive backend. - doesn't seem to work at all
#matplotlib.use('GTKAgg') # 'GTK' backend - can't seem to get this to work.... -

import matplotlib.pyplot as plt
import time
import numpy as np

plt.ion()

tstart = time.time()                     # for profiling
x = np.arange(0,2*np.pi,0.01)            # x-array
line, = plt.plot(x,np.sin(x))

#plt.ioff()

for i in np.arange(1,200):

    line.set_ydata(np.sin(x+i/10.0))  # update the data
    line.axes.set_title('frame number {0}'.format(i))

    plt.draw()                         # redraw the canvas

print 'FPS:' , 200/(time.time()-tstart)

编辑

编辑的代码 - 摆脱一些风格问题。

【问题讨论】:

  • Python 2.7 和 Linux 上的 matplotlib 1.1。对于它的价值,在这种情况下调用ioff 对性能没有影响(并且在循环中使用ion 没有额外的绘图调用)。但是,ioff 可能(即允许)导致后端的主循环停止(或者可能不会,确切的行为取决于后端)。这就是为什么我猜测问题是由于ioff 造成的,无论如何。如果你改用matplotlib.animations 模块会发生什么? matplotlib.org/api/animation_api.html(在这种情况下FuncAnimation 是最简单的。)
  • 另外,最好避免使用from pylab import *,除非您使用的是外壳程序,但在这种情况下,这纯粹是风格问题,不会影响您的问题。无论如何,请改用matplotlib.pyplot(惯例是import matplotlib.pyplot as plt,以避免代码过于冗长)。
  • 至于 matplotlib.animations 的东西......我之前快速浏览了一下 - 但我不太确定这是我想要的。基本上我可能是错的——但看起来它希望你创建一个由 matplotlib 重复调用的函数——更新每次显示的数据。我想要的是相反的方式 - 在这里我只是使用'sin'作为一个假人 - 但实际上在我的代码中我正在做数值积分......这需要相当长的时间+其他稍微改变分析的逻辑东西。我希望我的代码调用 matplot lib - 而不是相反。
  • pyplotpylab部分而言,pyplot只是matplotlib的核心,而pylabmatplotlibnumpymatplotlib.mlab全部滚动合而为一。这是一个巨大的命名空间,最好能区分事物的来源。当然,这主要是风格上的,但如果没有别的,它可以更容易地找到合适的帮助场所。
  • 再次感谢 - 恐怕我通常会使用 spyder 进行开发 - 但在这种情况下,导致问题的绝对不是 spyder - 我从 cmd 以及从 IPython 运行时都得到相同的效果.因此我怀疑它可能与 windows 上的 python\matplotlib 有关 - 因为它适用于你在 linux 上。我很喜欢使用 wx 做某事——这似乎在基本层面上可以工作——我想我会看到它在我的代码中的工作情况。

标签: python matplotlib plot interactive pythonxy


【解决方案1】:

好的...所以我整理了一些可能对我有用的东西....

基本上它就像一个淡化的 gui - 但我希望它是一个我可以导入的类,并且基本上忘记了(希望如此)的细节。

我应该说 - 这是我第一次尝试在 python 中使用线程或 gui - 所以这段代码带有健康警告。

** 不过,我不会将问题标记为已回答 - 因为我确信更有经验的人会有更好的解决方案。

'''

JP

Attempt to get multiple updating of matplotlibs working.
Uses WX to create an 'almost' gui with a mpl in the middle of it.
Data can be queued to this object - or you can directly plot to it.

Probably will have some limitations atm
- only really thinking about 2d plots for now -
but presumably can work around this for other implimentations.
- the working code seems to need to be put into another thread.
Tried to put the wx mainloop into another thread,
but it seemed unhappy. :(



Classes of Interest :
    GraphData - A silly class that holds data to be plotted.
    PlotFigure - Class of wx frame type.
        Holds a mpl figure in it + queue to queue data to.
        The frame will plot the data when it refreshes it's canvas

    ThreadSimulation - This is not to do with the plotting
                        it is a test program.


Modified version of:

Copyright (C) 2003-2005 Jeremy O'Donoghue and others

License: This work is licensed under the PSF. A copy should be included
with this source code, and is also available at
http://www.python.org/psf/license.html

'''
import threading
import collections
import time

import numpy as np

import matplotlib
matplotlib.use('WXAgg')



from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.backends.backend_wx import NavigationToolbar2Wx

from matplotlib.figure import Figure

import wx







class GraphData(object):
    '''
        A silly class that holds data to be plotted.
    '''
    def __init__(self, xdatainit, ydatainit):

        self.xdata = xdatainit
        self.ydata = ydatainit

class PlotFigure(wx.Frame):

    def __init__(self ):
        '''
            Initialises the frame.
        '''
        wx.Frame.__init__(self, None, -1, "Test embedded wxFigure")

        self.timerid = wx.NewId()

        self.fig = Figure((5,4), 75)
        self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
        self.toolbar = NavigationToolbar2Wx(self.canvas)
        self.toolbar.Realize()

        # On Windows, default frame size behaviour is incorrect
        # you don't need this under Linux
        tw, th = self.toolbar.GetSizeTuple()
        fw, fh = self.canvas.GetSizeTuple()
        self.toolbar.SetSize(wx.Size(fw, th))

        # Now put all into a sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        # This way of adding to sizer allows resizing
        sizer.Add(self.canvas, 1, wx.LEFT|wx.TOP|wx.GROW)
        # Best to allow the toolbar to resize!
        sizer.Add(self.toolbar, 0, wx.GROW)
        self.SetSizer(sizer)
        self.Fit()
        wx.EVT_TIMER(self, self.timerid, self.onTimer)

        self.dataqueue = collections.deque()

        # Add an axes and a line to the figure.
        self.axes = self.fig.add_subplot(111)
        self.line, = self.axes.plot([],[])

    def GetToolBar(self):
        '''
            returns default toolbar.
        '''
        return self.toolbar

    def onTimer(self, evt):
        '''
            Every timer period this is called.

            Want to redraw the canvas.
        '''
        #print "onTimer"
        if len(self.dataqueue) > 0 :
            data = self.dataqueue.pop()

            x = data.xdata
            y = data.ydata

            xmax = max(x)
            xmin = min(x)

            ymin = round(min(y), 0) - 1
            ymax = round(max(y), 0) + 1

            self.axes.set_xbound(lower=xmin, upper=xmax)
            self.axes.set_ybound(lower=ymin, upper=ymax)

            self.line.set_xdata(x)
            self.line.set_ydata(y)

        # Redraws the canvas - does this even if the data isn't updated...
        self.canvas.draw()


    def onEraseBackground(self, evt):
        '''
        this is supposed to prevent redraw flicker on some X servers...
        '''
        pass


class ThreadSimulation(threading.Thread):
    '''
    Simulation Thread - produces data to be displayed in the other thread.
    '''

    def __init__(self,  nsimloops, datastep, pltframe, slowloop = 0):
        threading.Thread.__init__(self)

        self.nsimloops = nsimloops
        self.datastep = datastep
        self.pltframe = pltframe
        self.slowloop=slowloop

    def run(self):
        '''
        This is the simulation function.
        '''
        nsimloops = self.nsimloops
        datastep = self.datastep
        pltframe = self.pltframe

        print 'Sim Thread: Starting.'
        tstart = time.time()               # for profiling

        # Define Data to share between threads.
        x  = np.arange(0,2*np.pi,datastep)            # x-array
        y  = np.sin(x )

        # Queues up the data and removes previous versions.
        pltframe.dataqueue.append(GraphData(x,y))
        for i in range(len(pltframe.dataqueue)-1):
            pltframe.dataqueue.popleft()
        pltframe.dataqueue

        for i in np.arange(1, nsimloops):


            x = x + datastep
            y = np.sin(x)

            # Queues up the data and removes previous versions.
            pltframe.dataqueue.append(GraphData(x,y))
            for i in range(len(pltframe.dataqueue)-1):
                pltframe.dataqueue.popleft()
            #pltframe.dataqueue

            if self.slowloop > 0 :
                time.sleep(self.slowloop)



        tstop= time.time()
        print 'Sim Thread: Complete.'
        print 'Av Loop Time:' , (tstop-tstart)/ nsimloops

if __name__ == '__main__':


    # Create the wx application.
    app = wx.PySimpleApp()

    # Create a frame with a plot inside it.
    pltframe = PlotFigure()
    pltframe1 = PlotFigure()

    # Initialise the timer - wxPython requires this to be connected to
    # the receiving event handler

    t = wx.Timer(pltframe, pltframe.timerid)
    t.Start(100)

    pltframe.Show()
    pltframe1.Show()

    npoints = 100
    nsimloops = 20000
    datastep = 2 * np.pi/ npoints
    slowloop = .1

    #Define and start application thread
    thrd = ThreadSimulation(nsimloops, datastep, pltframe,slowloop)
    thrd.setDaemon(True)
    thrd.start()

    pltframe1.axes.plot(np.random.rand(10),np.random.rand(10))

    app.MainLoop()

【讨论】:

  • 好的,所以我将其标记为已回答 - 因为有人提到我的很多问题都没有被标记为已回答,这使得我将来获得帮助的可能性降低......多么纠结的网络我们编织!
猜你喜欢
  • 2018-04-28
  • 1970-01-01
  • 2011-03-27
  • 2021-01-31
  • 1970-01-01
  • 2021-12-31
  • 2017-09-03
  • 1970-01-01
  • 2015-08-20
相关资源
最近更新 更多