嵌入 matplotlibAnimation
Embedding matplotlibAnimation
我正在设计一个应用程序,它必须通过 Arduino 和 Python 从传感器绘制序列。我正在使用 matplotlib 为我的图表制作动画,它可以很好地处理我昨天发布的问题中可以看到的代码:. Now, since I want to make a nice looking GUI, I want to embed my animation in PyQt5. For that I've taken as a reference this link https://pythonspot.com/en/pyqt5-matplotlib/ together with that one 。我的结果代码如下所示:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, QSizePolicy, QMessageBox, QWidget, \
QPushButton
from PyQt5.QtGui import QIcon
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import serial
import time
class App(QMainWindow):
def __init__(self):
super().__init__()
self.left = 10
self.top = 10
self.title = 'PyQt5 matplotlib example - pythonspot.com'
self.width = 640
self.height = 400
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
m = PlotCanvas(self, width=5, height=4)
m.move(0, 0)
button = QPushButton('PyQt5 button', self)
button.setToolTip('This is an example button')
button.move(500, 0)
button.resize(140, 100)
self.show()
class PlotCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
global fig
fig = Figure(figsize=(width, height), dpi=dpi)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
#self.axes = fig.add_subplot(111)#, IYV: can be removed
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.plot()
self.animate()
def plot(self):
global xar, yar, optimal_frequency, ser, ax1
ser = serial.Serial("com3", 2400)
ser.readline()
optimal_frequency = 100
ax1 = self.figure.add_subplot(111)
xar = []
yar = []
print(time.ctime())
def animate(self):
self.anim = animation.FuncAnimation(fig, self.animate_loop(), interval=optimal_frequency)
self.draw()
def animate_loop(self):
global xar, yar
ser.readline()
for i in range(optimal_frequency):
a = str(ser.readline(), 'utf-8')
try:
b = float(a)
except ValueError:
ser.readline()
xar.append(str(time.time()))
print(time.ctime())
yar.append(int(b))
ax1.clear()
ax1.plot(xar, yar)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
但我收到错误消息:
Traceback (most recent call last):
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 113, in <module>
ex = App()
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 35, in __init__
self.initUI()
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 41, in initUI
m = PlotCanvas(self, width=5, height=4)
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 71, in __init__
self.animate()
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 87, in animate
self.draw()
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_qt5agg.py", line 159, in draw
FigureCanvasAgg.draw(self)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_agg.py", line 474, in draw
self.figure.draw(self.renderer)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\artist.py", line 62, in draw_wrapper
draw(artist, renderer, *args, **kwargs)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\figure.py", line 1165, in draw
self.canvas.draw_event(renderer)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backend_bases.py", line 1809, in draw_event
self.callbacks.process(s, event)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\cbook.py", line 563, in process
proxy(*args, **kwargs)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\cbook.py", line 430, in __call__
return mtd(*args, **kwargs)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\animation.py", line 661, in _start
self._init_draw()
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\animation.py", line 1221, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\animation.py", line 1243, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
TypeError: 'NoneType' object is not callable
Exception ignored in: <bound method TimerQT.__del__ of <matplotlib.backends.backend_qt5.TimerQT object at 0x0000026C3260DD30>>
Traceback (most recent call last):
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_qt5.py", line 201, in __del__
TypeError: 'method' object is not connected
关于如何获得此 运行 的任何帮助?
干杯
正如您在 link 问题中看到的那样,FuncAnimation
需要一个方法作为其第二个参数。但是,在您的调用中,您改为提供 None
(因为 self.animate_loop()
的计算结果为 None
)。将其更改为
self.anim = animation.FuncAnimation(fig, self.animate_loop, interval=optimal_frequency)
其次,从 linked 问题中也可以看出,self.animate_loop
需要进行论证,因此您可能需要将其更改为
def animate_loop(self,i):
除此之外,您的代码中还有一些小问题,例如如果 b = float(a)
失败,则 b
未定义并且 yar.append(int(b))
将引发错误。
同样在 classes 中使用 global
似乎很奇怪;这不是问题,但会使代码难以阅读。最好使用 class 个变量。
非常感谢@ImportanceOfBeingErnest,解决了问题。我也考虑了您的批评者;我真的需要更好地了解面向对象的编程。
虽然 GUI 反应缓慢:window 无法轻松移动或调整大小,而且按钮很难按下。出于这个原因,我建议放弃 matplotlib 进行串行绘图,而改用 PyQtGraoh。下面的代码做同样的事情,只是用 PyQtGraph/PyQt4 而不是 Matplotlib/PyQt5。代码如下:
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import serial
import random
import time
app = QtGui.QApplication([])
p = pg.plot(title='random numbers generator')
curve = p.plot()
data = [0]
port = "com3"
baudrate = 600
ser = serial.Serial(port, baudrate)
ser.readline()
print(time.ctime())
def update():
global curve, data
a = str(ser.readline(), 'utf-8')
try:
data.append(float(a))
except ValueError:
ser.readline()
curve.setData(data) #xdata is not necessary
app.processEvents()
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
我真的很想使用 PyQt5 以便能够使用 PyQtDeploy 进行部署,但是我必须一直使用 PyInstaller,因为这种在 PyQt5 中包含 PyQtGraph 的方式对我来说似乎相当复杂:https://github.com/pyqtgraph/pyqtgraph/issues/33
我正在设计一个应用程序,它必须通过 Arduino 和 Python 从传感器绘制序列。我正在使用 matplotlib 为我的图表制作动画,它可以很好地处理我昨天发布的问题中可以看到的代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, QSizePolicy, QMessageBox, QWidget, \
QPushButton
from PyQt5.QtGui import QIcon
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import serial
import time
class App(QMainWindow):
def __init__(self):
super().__init__()
self.left = 10
self.top = 10
self.title = 'PyQt5 matplotlib example - pythonspot.com'
self.width = 640
self.height = 400
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
m = PlotCanvas(self, width=5, height=4)
m.move(0, 0)
button = QPushButton('PyQt5 button', self)
button.setToolTip('This is an example button')
button.move(500, 0)
button.resize(140, 100)
self.show()
class PlotCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
global fig
fig = Figure(figsize=(width, height), dpi=dpi)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
#self.axes = fig.add_subplot(111)#, IYV: can be removed
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.plot()
self.animate()
def plot(self):
global xar, yar, optimal_frequency, ser, ax1
ser = serial.Serial("com3", 2400)
ser.readline()
optimal_frequency = 100
ax1 = self.figure.add_subplot(111)
xar = []
yar = []
print(time.ctime())
def animate(self):
self.anim = animation.FuncAnimation(fig, self.animate_loop(), interval=optimal_frequency)
self.draw()
def animate_loop(self):
global xar, yar
ser.readline()
for i in range(optimal_frequency):
a = str(ser.readline(), 'utf-8')
try:
b = float(a)
except ValueError:
ser.readline()
xar.append(str(time.time()))
print(time.ctime())
yar.append(int(b))
ax1.clear()
ax1.plot(xar, yar)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
但我收到错误消息:
Traceback (most recent call last):
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 113, in <module>
ex = App()
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 35, in __init__
self.initUI()
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 41, in initUI
m = PlotCanvas(self, width=5, height=4)
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 71, in __init__
self.animate()
File "C:/Users/iyv/Documents/Udvikling/20161205_Serial_Plotter/Embedding_PyQt5/20161220_Embedding_Serial.py", line 87, in animate
self.draw()
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_qt5agg.py", line 159, in draw
FigureCanvasAgg.draw(self)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_agg.py", line 474, in draw
self.figure.draw(self.renderer)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\artist.py", line 62, in draw_wrapper
draw(artist, renderer, *args, **kwargs)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\figure.py", line 1165, in draw
self.canvas.draw_event(renderer)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backend_bases.py", line 1809, in draw_event
self.callbacks.process(s, event)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\cbook.py", line 563, in process
proxy(*args, **kwargs)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\cbook.py", line 430, in __call__
return mtd(*args, **kwargs)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\animation.py", line 661, in _start
self._init_draw()
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\animation.py", line 1221, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\animation.py", line 1243, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
TypeError: 'NoneType' object is not callable
Exception ignored in: <bound method TimerQT.__del__ of <matplotlib.backends.backend_qt5.TimerQT object at 0x0000026C3260DD30>>
Traceback (most recent call last):
File "C:\Users\iyv\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_qt5.py", line 201, in __del__
TypeError: 'method' object is not connected
关于如何获得此 运行 的任何帮助? 干杯
正如您在 link 问题中看到的那样,FuncAnimation
需要一个方法作为其第二个参数。但是,在您的调用中,您改为提供 None
(因为 self.animate_loop()
的计算结果为 None
)。将其更改为
self.anim = animation.FuncAnimation(fig, self.animate_loop, interval=optimal_frequency)
其次,从 linked 问题中也可以看出,self.animate_loop
需要进行论证,因此您可能需要将其更改为
def animate_loop(self,i):
除此之外,您的代码中还有一些小问题,例如如果 b = float(a)
失败,则 b
未定义并且 yar.append(int(b))
将引发错误。
同样在 classes 中使用 global
似乎很奇怪;这不是问题,但会使代码难以阅读。最好使用 class 个变量。
非常感谢@ImportanceOfBeingErnest,解决了问题。我也考虑了您的批评者;我真的需要更好地了解面向对象的编程。
虽然 GUI 反应缓慢:window 无法轻松移动或调整大小,而且按钮很难按下。出于这个原因,我建议放弃 matplotlib 进行串行绘图,而改用 PyQtGraoh。下面的代码做同样的事情,只是用 PyQtGraph/PyQt4 而不是 Matplotlib/PyQt5。代码如下:
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import serial
import random
import time
app = QtGui.QApplication([])
p = pg.plot(title='random numbers generator')
curve = p.plot()
data = [0]
port = "com3"
baudrate = 600
ser = serial.Serial(port, baudrate)
ser.readline()
print(time.ctime())
def update():
global curve, data
a = str(ser.readline(), 'utf-8')
try:
data.append(float(a))
except ValueError:
ser.readline()
curve.setData(data) #xdata is not necessary
app.processEvents()
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
我真的很想使用 PyQt5 以便能够使用 PyQtDeploy 进行部署,但是我必须一直使用 PyInstaller,因为这种在 PyQt5 中包含 PyQtGraph 的方式对我来说似乎相当复杂:https://github.com/pyqtgraph/pyqtgraph/issues/33