【问题标题】:Embedding matplotlibAnimation嵌入 matplotlib 动画
【发布时间】:2017-05-05 15:03:18
【问题描述】:

我正在设计一个应用程序,该应用程序必须通过 Arduino 和 Python 绘制来自传感器的序列。我正在使用 matplotlib 为我的图表制作动画,它与我昨天发布的一个问题中可以看到的代码配合得很好:Arduino Live Serial Plotting with a MatplotlibAnimation gets slow。现在,因为我想制作一个漂亮的 GUI,我想在 PyQt5 中嵌入我的动画。为此,我将这个链接https://pythonspot.com/en/pyqt5-matplotlib/ 和那个Getting blitting to work in funcAnimation embedded in PyQT4 GUI 作为参考。我生成的代码如下所示:

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

关于如何运行此程序的任何帮助? 干杯

【问题讨论】:

标签: python matplotlib pyqt pyqt5 pyserial


【解决方案1】:

您也可以在链接到的问题中看到,FuncAnimation 需要一个方法作为其第二个参数。但是,在您的通话中,您改为提供None(因为self.animate_loop() 的计算结果为None)。将其更改为

self.anim = animation.FuncAnimation(fig, self.animate_loop, interval=optimal_frequency)

其次,从链接的问题也可以看出,self.animate_loop 需要一个参数,所以可能你需要将其更改为

def animate_loop(self,i):

除此之外,您的代码中还有一些小问题,例如如果b = float(a) 失败,b 未定义,yar.append(int(b)) 将引发错误。 同样在类中使用global 似乎很奇怪;这不是问题,但会使代码难以阅读。更好地使用类变量。

【讨论】:

    【解决方案2】:

    非常感谢@ImportanceOfBeingErnest,这解决了问题。我也考虑到你的批评;我真的需要更好地了解面向对象的编程。

    虽然 GUI 响应缓慢:窗口不能轻易移动或调整大小,而且按钮很难按下。出于这个原因,我建议放弃 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-07
      • 2020-07-21
      • 2021-02-01
      • 2023-03-15
      • 2021-01-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多