matplotlib
Animacje i interaktywne kreślenie
Szukaj…
Wprowadzenie
Dzięki Python Matplotlib możesz poprawnie tworzyć animowane wykresy.
Podstawowa animacja z FuncAnimation
Pakiet matplotlib.animation oferuje pewne klasy do tworzenia animacji. FuncAnimation
tworzy animacje poprzez wielokrotne wywoływanie funkcji. Tutaj używamy funkcji animate()
która zmienia współrzędne punktu na wykresie funkcji sinusoidalnej.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
TWOPI = 2*np.pi
fig, ax = plt.subplots()
t = np.arange(0.0, TWOPI, 0.001)
s = np.sin(t)
l = plt.plot(t, s)
ax = plt.axis([0,TWOPI,-1,1])
redDot, = plt.plot([0], [np.sin(0)], 'ro')
def animate(i):
redDot.set_data(i, np.sin(i))
return redDot,
# create animation using the animate() function
myAnimation = animation.FuncAnimation(fig, animate, frames=np.arange(0.0, TWOPI, 0.1), \
interval=10, blit=True, repeat=True)
plt.show()
Zapisz animację do gif
W tym przykładzie używamy save
sposobu zapisania Animation
obiektu przy użyciu ImageMagick.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import rcParams
# make sure the full paths for ImageMagick and ffmpeg are configured
rcParams['animation.convert_path'] = r'C:\Program Files\ImageMagick\convert'
rcParams['animation.ffmpeg_path'] = r'C:\Program Files\ffmpeg\bin\ffmpeg.exe'
TWOPI = 2*np.pi
fig, ax = plt.subplots()
t = np.arange(0.0, TWOPI, 0.001)
s = np.sin(t)
l = plt.plot(t, s)
ax = plt.axis([0,TWOPI,-1,1])
redDot, = plt.plot([0], [np.sin(0)], 'ro')
def animate(i):
redDot.set_data(i, np.sin(i))
return redDot,
# create animation using the animate() function with no repeat
myAnimation = animation.FuncAnimation(fig, animate, frames=np.arange(0.0, TWOPI, 0.1), \
interval=10, blit=True, repeat=False)
# save animation at 30 frames per second
myAnimation.save('myAnimation.gif', writer='imagemagick', fps=30)
Interaktywne elementy sterujące z matplotlib.widgets
Do interakcji z wykresami Matplotlib oferuje neutralne dla GUI widżety . Widżety wymagają obiektu matplotlib.axes.Axes
.
Oto demo widgetu z suwakiem, który aktualizuje amplitudę krzywej sinusoidalnej. Funkcja aktualizacji jest uruchamiana przez zdarzenie on_changed()
suwaka.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.widgets import Slider
TWOPI = 2*np.pi
fig, ax = plt.subplots()
t = np.arange(0.0, TWOPI, 0.001)
initial_amp = .5
s = initial_amp*np.sin(t)
l, = plt.plot(t, s, lw=2)
ax = plt.axis([0,TWOPI,-1,1])
axamp = plt.axes([0.25, .03, 0.50, 0.02])
# Slider
samp = Slider(axamp, 'Amp', 0, 1, valinit=initial_amp)
def update(val):
# amp is the current value of the slider
amp = samp.val
# update curve
l.set_ydata(amp*np.sin(t))
# redraw canvas while idle
fig.canvas.draw_idle()
# call update function on slider value change
samp.on_changed(update)
plt.show()
- AxesWidget
- Przycisk
- CheckButtons
- Kursor
- EllipseSelector
- Lasso
- LassoSelector
- LockDraw
- MultiCursor
- Przyciski radiowe
- RectangleSelector
- SpanSelector
- SubplotTool
- Uchwyty narzędzi
Rysuj dane na żywo z potoku za pomocą Matplotlib
Może to być przydatne, gdy chcesz wizualizować przychodzące dane w czasie rzeczywistym. Dane te mogą na przykład pochodzić z mikrokontrolera, który stale próbkuje sygnał analogowy.
W tym przykładzie otrzymamy nasze dane z nazwanego potoku (znanego również jako fifo). W tym przykładzie dane w potoku powinny być liczbami oddzielonymi znakami nowej linii, ale można to dostosować do własnych upodobań.
Przykładowe dane:
100
123.5
1589
Więcej informacji o nazwanych potokach
Będziemy również używać typu danych deque ze standardowych zbiorów bibliotecznych. Obiekt deque działa dość podobnie do listy. Ale w przypadku obiektu deque dość łatwo jest do niego dołączyć, utrzymując obiekt deque na stałej długości. To pozwala nam utrzymać oś x na stałej długości zamiast zawsze powiększać i ściskać wykres razem. Więcej informacji o obiektach deque
Wybór odpowiedniego backendu ma kluczowe znaczenie dla wydajności. Sprawdź, jakie backendy działają w twoim systemie operacyjnym i wybierz szybki. Dla mnie działał tylko qt4agg i domyślny backend, ale domyślny był zbyt wolny. Więcej informacji o backendach w matplotlib
Ten przykład jest oparty na matplotlibowym schemacie losowych danych .
Żaden ze znaków w tym kodzie nie powinien zostać usunięty.
import matplotlib
import collections
#selecting the right backend, change qt4agg to your desired backend
matplotlib.use('qt4agg')
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#command to open the pipe
datapipe = open('path to your pipe','r')
#amount of data to be displayed at once, this is the size of the x axis
#increasing this amount also makes plotting slightly slower
data_amount = 1000
#set the size of the deque object
datalist = collections.deque([0]*data_amount,data_amount)
#configure the graph itself
fig, ax = plt.subplots()
line, = ax.plot([0,]*data_amount)
#size of the y axis is set here
ax.set_ylim(0,256)
def update(data):
line.set_ydata(data)
return line,
def data_gen():
while True:
"""
We read two data points in at once, to improve speed
You can read more at once to increase speed
Or you can read just one at a time for improved animation smoothness
data from the pipe comes in as a string,
and is seperated with a newline character,
which is why we use respectively eval and rstrip.
"""
datalist.append(eval((datapipe.readline()).rstrip('\n')))
datalist.append(eval((datapipe.readline()).rstrip('\n')))
yield datalist
ani = animation.FuncAnimation(fig,update,data_gen,interval=0, blit=True)
plt.show()
Jeśli po pewnym czasie wykres zacznie się opóźniać, spróbuj dodać więcej danych datalist.append, aby więcej linii było czytanych w każdej ramce. Lub wybierz szybszy backend, jeśli możesz.
To działało z danymi 150 Hz z rury na moim 1,7 GHz i3 4005u.