Интерактивное построение Python Matplotlib - зависает через несколько кадров

У меня проблема с тем, чтобы matplotlib хорошо работал с интерактивным построением графиков ... я вижу, что после отображения нескольких кадров моих смоделированных данных matplotlib зависает и больше не отображается.

В основном я немного поигрался с научным моделированием - и хотел бы иметь возможность отображать свои результаты по мере их получения - а не в конце - с помощью pylab.show ().

Некоторое время назад я нашел пример из поваренной книги, который, кажется, делает то, что я хотел - простыми словами (хотя, очевидно, данные другие). Кулинарная книга находится здесь ... http://www.scipy.org/Cookbook/Matplotlib/Animations#head-2f6224cc0c133b6e35c95f4b74b1b6fc7d3edca4

Я немного поискал и знаю, что у некоторых людей были эти проблемы раньше - Анимация Matplotlib либо зависает через несколько кадров, либо просто не работает, но, похоже, в то время не было хороших решений. Мне было интересно, нашел ли кто-нибудь здесь хорошее решение.

Я пробовал несколько «бэкэндов» на 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)

ИЗМЕНИТЬ:

отредактированный код - чтобы избавиться от некоторых возникших проблем со стилем.


person JPH    schedule 24.11.2012    source источник
comment
Почему вы звоните ioff? (Нет очевидной причины для этого.) Будет ли у вас такое же поведение, если вы не позвоните ioff? Я не могу воспроизвести ваши проблемы, чего бы это ни стоило, но рискну предположить, что это потому, что вы звоните ioff, а потом рисуете сюжет.   -  person Joe Kington    schedule 26.11.2012
comment
Спасибо за ответ .... Думаю, причина, по которой я звонил ioff, заключалась в том, что, насколько мне известно, в интерактивном режиме, когда вы меняете что-то на графике, это вызывает перерисовку. то, что я пытаюсь сделать здесь, - это минимизировать количество перерисовок, чтобы мои симуляции не сильно замедлялись. В любом случае - его удаление не помогает - по крайней мере, мне - интересно, что вы можете запустить его нормально. Могу я спросить, какую версию Python и т. Д. Вы используете?   -  person JPH    schedule 26.11.2012
comment
Python 2.7 с matplotlib 1.1 в Linux. Как бы то ни было, в этом случае нет никакой разницы в производительности при вызове ioff (и никаких дополнительных вызовов отрисовки внутри вашего цикла с ion). Однако ioff может (т. Е. Разрешено) вызвать остановку основного цикла серверной части (или не может, точное поведение зависит от серверной части). Вот почему я предполагал, что проблема связана с ioff, во всяком случае. Что произойдет, если вместо этого вы воспользуетесь модулем matplotlib.animations? matplotlib.org/api/animation_api.html (в этом случае проще всего использовать FuncAnimation.)   -  person Joe Kington    schedule 26.11.2012
comment
Кроме того, действительно лучше избегать from pylab import *, если вы не используете что-то из оболочки, но это чисто стилистическая проблема и в данном случае не повлияет на вашу проблему. В любом случае используйте вместо этого matplotlib.pyplot (соглашение - import matplotlib.pyplot as plt, чтобы избежать чрезмерно многословного кода).   -  person Joe Kington    schedule 26.11.2012
comment
Спасибо ... Я использую python 2.7.3 и mpl 1.1.1, но Windows 7. Я понимаю вашу точку зрения по поводу стилистики - я обычно избегаю * s, но я искажал пример, который я получил через t'interweb, поэтому некоторые закрались плохие вещи. Есть ли причина использовать pyplot, а не pylab?   -  person JPH    schedule 26.11.2012
comment
Что касается материала matplotlib.animations ... Я бегло просмотрел это раньше, но я не совсем уверен, что это то, что я хочу. В принципе, я могу ошибаться - но похоже, что вы ожидаете, что вы создадите функцию, которая многократно вызывается matplotlib - каждый раз обновляя данные, отображаемые. Я хочу наоборот - здесь я просто использовал «грех» как фиктивную фигуру - но на самом деле в моем коде я использую числовые интегралы ... что занимает довольно много времени + другие логические вещи, которые немного меняют анализ. Я хочу, чтобы мой код вызывал matplot lib, а не наоборот.   -  person JPH    schedule 26.11.2012
comment
Что касается части pyplot vs pylab, pyplot - это просто ядро ​​matplotlib, тогда как pylab - это matplotlib, numpy и matplotlib.mlab в одном лице. Это гигантское пространство имен, и действительно лучше различать, откуда что происходит. Конечно, все это в основном стилистическое, но это значительно упрощает поиск подходящего места для помощи, если не что иное.   -  person Joe Kington    schedule 26.11.2012
comment
Что касается анимации, дайте мне немного и позвольте мне сколотить пример, который может помочь. (Вы правы, вам не нужно FuncAnimation, если вы хотите вызвать обновление, но не слишком сложно сделать наоборот.) Я снимаю в темноте, так как не вижу такой же проблемы на linux, но, во всяком случае, я могу сделать несколько предположений.   -  person Joe Kington    schedule 26.11.2012
comment
На самом деле, если то, что у вас есть в вашей новой версии, работает некорректно, это ошибка. Мой пример должен был быть просто более сложной версией того, что у вас есть ... Поскольку вы упомянули pythonxy, вы случайно не запускаете его из Spyder? Если да, то это может быть проблема с тем, как spyder обрабатывает потоки с matplotlib ... У вас возникнет такая же проблема, если вы запустите его напрямую? (например, откройте окно cmd и запустите python filename.py)   -  person Joe Kington    schedule 26.11.2012
comment
Еще раз спасибо - боюсь, я обычно использую spyder для разработки - но в этом случае это определенно НЕ spyder, который вызывает проблемы - я получаю тот же эффект от cmd, а также от IPython. Я подозреваю, что это может быть связано с python \ matplotlib в Windows - поскольку он работает для вас в Linux. У меня был опыт, когда я что-то делал с помощью wx - который, кажется, работает на базовом уровне - думаю, я посмотрю, насколько хорошо это работает в моем коде.   -  person JPH    schedule 26.11.2012


Ответы (1)


Хорошо ... Итак, я собрал кое-что, что может мне помочь ....

По сути, это что-то вроде разбавленного графического интерфейса, но я надеюсь, что это класс, который я могу импортировать и в основном забыть о деталях (вот надеюсь).

Я должен сказать, что это моя первая попытка распараллелить ИЛИ guis на python, поэтому этот код содержит предупреждение о вреде для здоровья.

** Я не собираюсь отмечать этот вопрос как ответ, потому что уверен, что у более опытного найдется лучшее решение.

'''

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()
person JPH    schedule 26.11.2012
comment
Хорошо, поэтому я отметил, что это ответ - так как кто-то упомянул, что многие мои вопросы не были отмечены ответами, и уменьшили вероятность того, что я получу помощь в будущем ... Какую запутанную паутину мы плетем! - person JPH; 03.03.2013