2012-06-08 3 views
72

Я делаю приложение на Python, которое собирает данные из последовательного порта и отображает график собранных данных по времени прибытия. Время прибытия для данных не определено. Я хочу, чтобы сюжет обновлялся при получении данных. Я искал, как это сделать, и нашел два метода:Динамическое обновление участка в matplotlib

  1. Очистить участок и снова перерисовать участок со всеми точками.
  2. Анимация сюжета путем изменения его после определенного интервала.

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

Есть ли способ, с помощью которого можно обновить сюжет, просто добавив к нему больше очков, только когда данные будут получены?

+1

Возможный дубликат [В режиме реального времени построение графика в цикле с matplotlib] (http://stackoverflow.com/questions/11874767/real-time-plotting-in-while-loop-with-matplotlib) –

ответ

92

Есть ли способ, в котором я могу обновить сюжет, просто добавив несколько точек [с] к нему ...

Есть несколько способов анимации данных в Matplotlib, в зависимости от версии, которую вы имеете. Вы видели примеры matplotlib cookbook? Кроме того, ознакомьтесь с более современным animation examples в документации matplotlib. Наконец, определяет функцию FuncAnimation, которая анимирует функцию во времени. Эта функция может быть просто функцией, которую вы используете для получения ваших данных.

Каждый метод в основном устанавливает свойство data объекта, нарисованного, поэтому не требует очистки экрана или рисунка. Свойство data может быть просто расширено, чтобы вы могли сохранить предыдущие баллы и просто добавлять в свою линию (или изображение или то, что вы рисуете).

Учитывая, что вы говорите, что ваше время прибытия данных неопределенное ваш лучший выбор, вероятно, просто сделать что-то вроде:

import matplotlib.pyplot as plt 
import numpy 

hl, = plt.plot([], []) 

def update_line(hl, new_data): 
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data)) 
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data)) 
    plt.draw() 

Затем, когда вы получаете данные из последовательного порта просто позвоните update_line.

+0

Наконец-то! Я искал ответ на этот +1 :) Как сделать график автоматически масштабироваться. ax.set_autoscale_on (True), похоже, не работает. –

+11

Нашел ответ: вызовите ax.relim(), затем ax.autoscale_view() после обновления данных, но перед вызовом plt.draw() –

+0

Это не работает должным образом для меня –

19

Для этого без использования FuncAnimation (например, вы хотите выполнить другие части кода во время создания графика или хотите обновить несколько графиков одновременно), вызывающий только draw не создает сюжет (по крайней мере, с бэкэндом qt).

следующие работы для меня:

import matplotlib.pyplot as plt 
plt.ion() 
class DynamicUpdate(): 
    #Suppose we know the x range 
    min_x = 0 
    max_x = 10 

    def on_launch(self): 
     #Set up plot 
     self.figure, self.ax = plt.subplots() 
     self.lines, = self.ax.plot([],[], 'o') 
     #Autoscale on unknown axis and known lims on the other 
     self.ax.set_autoscaley_on(True) 
     self.ax.set_xlim(self.min_x, self.max_x) 
     #Other stuff 
     self.ax.grid() 
     ... 

    def on_running(self, xdata, ydata): 
     #Update data (with the new _and_ the old points) 
     self.lines.set_xdata(xdata) 
     self.lines.set_ydata(ydata) 
     #Need both of these in order to rescale 
     self.ax.relim() 
     self.ax.autoscale_view() 
     #We need to draw *and* flush 
     self.figure.canvas.draw() 
     self.figure.canvas.flush_events() 

    #Example 
    def __call__(self): 
     import numpy as np 
     import time 
     self.on_launch() 
     xdata = [] 
     ydata = [] 
     for x in np.arange(0,10,0.5): 
      xdata.append(x) 
      ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2)) 
      self.on_running(xdata, ydata) 
      time.sleep(1) 
     return xdata, ydata 

d = DynamicUpdate() 
d() 
+0

Да! Наконец, решение, которое работает с Spyder! Вещь, которую я отсутствовала, была gcf(). Canvas.flush_events() после команды draw(). – np8

1

Я знаю, что поздно, чтобы ответить на этот вопрос, но ваш вопрос вы можете заглянуть в пакет «джойстик». Я разработал его для построения потока данных из последовательного порта, но он работает для любого потока. Он также позволяет осуществлять интерактивное ведение журнала или построение изображений (в дополнение к построению графика). Не нужно делать собственные петли в отдельном потоке, пакет позаботится об этом, просто дайте частоту обновления, которую вы хотите. Кроме того, терминал остается доступным для контроля команд при построении графика. См http://www.github.com/ceyzeriat/joystick/ или https://pypi.python.org/pypi/joystick (используйте пункт установить джойстик для установки)

Просто замените np.random.random() вашей реальной точки данных, считанных из последовательного порта в коде ниже:

import joystick as jk 
import numpy as np 
import time 

class test(jk.Joystick): 
    # initialize the infinite loop decorator 
    _infinite_loop = jk.deco_infinite_loop() 

    def _init(self, *args, **kwargs): 
     """ 
     Function called at initialization, see the doc 
     """ 
     self._t0 = time.time() # initialize time 
     self.xdata = np.array([self._t0]) # time x-axis 
     self.ydata = np.array([0.0]) # fake data y-axis 
     # create a graph frame 
     self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1))) 

    @_infinite_loop(wait_time=0.2) 
    def _generate_data(self): # function looped every 0.2 second to read or produce data 
     """ 
     Loop starting with the simulation start, getting data and 
    pushing it to the graph every 0.2 seconds 
     """ 
     # concatenate data on the time x-axis 
     self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax) 
     # concatenate data on the fake data y-axis 
     self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax) 
     self.mygraph.set_xydata(t, self.ydata) 

t = test() 
t.start() 
t.stop() 
Смежные вопросы