在课堂上制作 pyqtgraph 动画

use*_*808 2 python pyqtgraph

我正在尝试编写一个程序,通过串口从 arduino 获取串行数据,并实时绘制它。我使用 matplotlib 编写了代码,但我希望对结果感到满意,所以我试图让它在 pyqtgraph 上工作(学习如何使用它的资源要少得多)。我的问题是代码显示了一个空图。似乎 _update 仅被调用一次,但是当我将其放入循环中时,图表甚至不显示。

\n\n

我编写了一些其他代码来实现我想要的功能,即实时绘制数据,在数据通过阈值后,它在数据上绘制新线,显示线性回归。我从这里得到了一个示例(https://github.com/JaFeKl/joystick_real_time_plot_with_pyqtgraph/blob/master/real_time_plot.py),因为我希望我的代码是可调用的(在函数中,但我无法让它工作.到目前为止,我正在从 python 中生成数据以简化调试

\n\n
import sys\nimport pyqtgraph as pg\nimport pyqtgraph.exporters\nfrom pyqtgraph.Qt import QtGui, QtCore\nimport numpy as np\n\nimport serial\n\n# test\nimport math\nimport time\n\n\nclass Graph(QtGui.QMainWindow):\n    def __init__(self, parent=None):\n        super(Graph, self).__init__(parent)\n        self.n = 3\n        self.mainbox = QtGui.QWidget()\n        self.setCentralWidget(self.mainbox)\n        self.mainbox.setLayout(QtGui.QVBoxLayout())\n\n        self.canvas = pg.GraphicsLayoutWidget()             # create GrpahicsLayoutWidget obejct  \n        self.mainbox.layout().addWidget(self.canvas)\n\n        #  Set up plot\n        self.analogPlot = self.canvas.addPlot(title=\'Signal from serial port\')\n        self.analogPlot.setYRange(-1,1123)                # set axis range\n        self.analogPlot.setXRange(-1,1123)\n        self.analogPlot.showGrid(x=True, y=True, alpha=0.5) # show Grid\n        x_axis = self.analogPlot.getAxis(\'bottom\')\n        y_axis = self.analogPlot.getAxis(\'left\')\n        font=QtGui.QFont()\n        font.setPixelSize(20)\n        x_axis.tickFont = font\n        y_axis.tickFont = font\n        x_axis.setLabel(text=\'Tens\xc3\xa3o [V]\')              # set axis labels\n        y_axis.setLabel(text=\'Corrente [mA]\')\n\n        self.plts = []\n        self.intplts = []\n        colors = [\'r\', \'b\', \'w\', \'y\', \'g\', \'m\', \'c\', \'k\']\n        for i in range(self.n):\n            self.plts.append([])\n            self.intplts.append([])\n\n\n        for i in range(self.n):\n            if len(self.plts) <= len(colors):\n                self.plts[i]=(self.analogPlot.plot(pen= pg.mkPen(colors[i], width=6)))\n        for i in range(self.n):\n            if len(self.plts) <= len(colors)*2:\n                self.intplts.append(self.analogPlot.plot(pen= pg.mkPen(colors[i+3], width=3)))\n\n        #Data\n        self.datay = []\n        self.datax = []\n        for i in range(self.n):\n            self.datax.append([])\n            self.datay.append([])\n\n        # set up image exporter (necessary to be able to export images)\n        QtGui.QApplication.processEvents()\n        self.exporter=pg.exporters.ImageExporter(self.canvas.scene())\n        self.image_counter = 1\n\n\n        # start updating\n        self.t=0\n\n        self._update()\n\n\n    def _update(self):\n        time.sleep(0.01)\n        if self.t<= 30:\n            #line = raw.readline()\n            #data.append(int(line))\n            self.datay[0].append(math.sin(self.t+(math.pi/2)))\n            self.datay[1].append(math.sin(self.t+(5*math.pi/4)))\n            self.datay[2].append(math.sin(self.t))\n            self.datax[0].append(self.t)\n            self.datax[1].append(self.t)\n            self.datax[2].append(self.t)\n            self.t+=0.1\n            self.plts[0].setData(self.datax[0], self.datay[0])\n            self.plts[1].setData(self.datax[1], self.datay[1])\n            self.plts[2].setData(self.datax[2], self.datay[2])\n\n            app.processEvents()\n        elif self.t>=30 and self.t<=30.1 :\n            self.t+=1\n\nif __name__ == \'__main__\':\n\n    app = QtGui.QApplication(sys.argv)\n    plot = Graph()\n    plot.show()\n    sys.exit(app.exec_())\n
Run Code Online (Sandbox Code Playgroud)\n\n

我期望结果与此代码类似(仅没有线性回归)

\n\n
import pyqtgraph as pg\nimport pyqtgraph.exporters\nfrom pyqtgraph.Qt import QtGui, QtCore\nimport numpy as np\n\n# linear regression\nfrom scipy import stats\n\n#Arduino\n#import find_arduino\n#import find_buad\nimport serial\n\nimport math\nimport time\n\n#port = find_arduino.FindArduino()\n#baud = find_buad.FindBaudRate()\nard=None\n\ndef Con():\n    global ard\n    ard = serial.Serial(port,baud,timeout=5)\n    time.sleep(2) # wait for Arduino\n    ard.close()\n\n# define the data\ntheTitle = "pyqtgraph plot"\ndatay = [[],[],[]]\ndatax = [[],[],[]]\nx2 = []\nT=[]\nt=0\n\ny1L=[]\nx1L=[]\n\n# create plot\n### START QtApp #####\napp = QtGui.QApplication([])            # you MUST do this once (initialize things)\n####################\nwin = pg.GraphicsWindow(title="Signal from serial port") # creates a window\nplt = win.addPlot(title="Realtime plot")  # creates empty space for the plot in the window\n\nfont=QtGui.QFont()\nfont.setPixelSize(20)\nplt.getAxis("bottom").tickFont = font\nplt.getAxis("left").tickFont = font\n\nplt1 = plt.plot(pen=pg.mkPen(\'r\', width=6))\nplt2= plt.plot(pen=pg.mkPen(\'b\', width=6))\nplt3= plt.plot(pen=pg.mkPen(\'w\', width=6))\n\nplt1I = plt.plot(pen=pg.mkPen(\'y\', width=3))\nplt2I = plt.plot(pen=pg.mkPen(\'g\', width=3))\nplt3I = plt.plot(pen=pg.mkPen(\'m\', width=3))\n\nplt.showGrid(x=True,y=True)\n\n\ndef update():\n    global plt1,plt2,plt3, t, plt1I, plt2I, plt3I\n    if t<= 30:\n        #line = raw.readline()\n        #data.append(int(line))\n        datay[0].append(math.sin(t+(math.pi/2)))\n        datay[1].append(math.sin(t+(5*math.pi/4)))\n        datay[2].append(math.sin(t))\n        datax[0].append(t)\n        datax[1].append(t)\n        datax[2].append(t)\n        t+=0.1\n        plt1.setData(datax[0],datay[0])\n        plt2.setData(datax[1],datay[1])\n        plt3.setData(datax[2],datay[2])\n        app.processEvents()\n        time.sleep(0.01)\n    elif t>=30 and t<=30.1 :\n        #plt1I.setData([0,1,2],[5,3,1])\n        #app.processEvents()\n        interp(plt1I, plt2I, plt3I)\n        t+=1\n    else:\n        app.processEvents()\n\ndef interp(pt1, pt2, pt3):\n    slope, intercept, r_value, p_value, std_err = stats.linregress(datax[0][10:],datay[0][10:])\n    x=[]\n    y=[]\n    print(slope)\n    for i in datax[0][10:]:\n        x.append(i)\n        y.append(intercept+slope*i)\n    pt1.setData(x,y)\n\n    slope, intercept, r_value, p_value, std_err = stats.linregress(datax[1][10:],datay[1][10:])\n    x=[]\n    y=[]\n    print(slope)\n    for i in datax[0][10:]:\n        x.append(i)\n        y.append(intercept+slope*i)\n    pt2.setData(x, y)\n\n    slope, intercept, r_value, p_value, std_err = stats.linregress(datax[2][10:],datay[2][10:])\n    x=[]\n    y=[]\n    print(slope)\n    for i in datax[0][10:]:\n        x.append(i)\n        y.append(intercept+slope*i)\n    pt3.setData(x,y)\n    app.processEvents()\n\ntimer = QtCore.QTimer()\ntimer.timeout.connect(update)\ntimer.start(0)\n\n### MAIN PROGRAM #####    \n# this is a brutal infinite loop calling your realtime data plot\n# make this interpret the incoming data\n#Con()\n#Communicate(1)\nwhile True: update()\n\n\n### END QtApp ####\npg.QtGui.QApplication.exec_() # you MUST put this at the end\n##################\n
Run Code Online (Sandbox Code Playgroud)\n

nat*_*ncy 5

在此输入图像描述

我没有连接 Arduino 来获取数据,因此在这个例子中我使用随机数据来绘制。绘制数据时,您希望避免使用,time.sleep()因为它会导致 GUI 冻结。相反,使用QtGui.QTimer()连接到更新处理程序来绘制数据。另外,作为一种优化,您可以使用线程轮询数据,然后在单独的计时器中更新它。

from pyqtgraph.Qt import QtCore, QtGui
from threading import Thread
import pyqtgraph as pg
import numpy as np
import random
import sys
import time

"""Scrolling Plot Widget Example"""

# Scrolling plot widget with adjustable X-axis and dynamic Y-axis
class ScrollingPlot(QtGui.QWidget):
    def __init__(self, parent=None):
        super(ScrollingPlot, self).__init__(parent)

        # Desired Frequency (Hz) = 1 / self.FREQUENCY
        # USE FOR TIME.SLEEP (s)
        self.FREQUENCY = .004

        # Frequency to update plot (ms)
        # USE FOR TIMER.TIMER (ms)
        self.TIMER_FREQUENCY = self.FREQUENCY * 1000

        # Set X Axis range. If desired is [-10,0] then set LEFT_X = -10 and RIGHT_X = 0
        self.LEFT_X = -10
        self.RIGHT_X = 0
        self.X_Axis = np.arange(self.LEFT_X, self.RIGHT_X, self.FREQUENCY)
        self.buffer = int((abs(self.LEFT_X) + abs(self.RIGHT_X))/self.FREQUENCY)
        self.data = [] 

        # Create Plot Widget 
        self.scrolling_plot_widget = pg.PlotWidget()

        # Enable/disable plot squeeze (Fixed axis movement)
        self.scrolling_plot_widget.plotItem.setMouseEnabled(x=False, y=False)
        self.scrolling_plot_widget.setXRange(self.LEFT_X, self.RIGHT_X)
        self.scrolling_plot_widget.setTitle('Scrolling Plot Example')
        self.scrolling_plot_widget.setLabel('left', 'Value')
        self.scrolling_plot_widget.setLabel('bottom', 'Time (s)')

        self.scrolling_plot = self.scrolling_plot_widget.plot()
        self.scrolling_plot.setPen(197,235,255)

        self.layout = QtGui.QGridLayout()
        self.layout.addWidget(self.scrolling_plot_widget)

        self.read_position_thread()
        self.start()

    # Update plot
    def start(self):
        self.position_update_timer = QtCore.QTimer()
        self.position_update_timer.timeout.connect(self.plot_updater)
        self.position_update_timer.start(self.get_scrolling_plot_timer_frequency())

    # Read in data using a thread
    def read_position_thread(self):
        self.current_position_value = 0
        self.old_current_position_value = 0
        self.position_update_thread = Thread(target=self.read_position, args=())
        self.position_update_thread.daemon = True
        self.position_update_thread.start()

    def read_position(self):
        frequency = self.get_scrolling_plot_frequency()
        while True:
            try:
                # Add data
                self.current_position_value = random.randint(1,101) 
                self.old_current_position_value = self.current_position_value
                time.sleep(frequency)
            except:
                self.current_position_value = self.old_current_position_value

    def plot_updater(self):
        self.dataPoint = float(self.current_position_value)

        if len(self.data) >= self.buffer:
            del self.data[:1]
        self.data.append(self.dataPoint)
        self.scrolling_plot.setData(self.X_Axis[len(self.X_Axis) - len(self.data):], self.data)

    def clear_scrolling_plot(self):
        self.data[:] = []

    def get_scrolling_plot_frequency(self):
        return self.FREQUENCY

    def get_scrolling_plot_timer_frequency(self):
        return self.TIMER_FREQUENCY

    def get_scrolling_plot_layout(self):
        return self.layout

    def get_current_position_value(self):
        return self.current_position_value

    def get_scrolling_plot_widget(self):
        return self.scrolling_plot_widget

if __name__ == '__main__':
    # Create main application window
    app = QtGui.QApplication([])
    app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
    mw = QtGui.QMainWindow()
    mw.setWindowTitle('Scrolling Plot Example')

    # Create scrolling plot
    scrolling_plot_widget = ScrollingPlot()

    # Create and set widget layout
    # Main widget container
    cw = QtGui.QWidget()
    ml = QtGui.QGridLayout()
    cw.setLayout(ml)
    mw.setCentralWidget(cw)

    # Can use either to add plot to main layout
    #ml.addWidget(scrolling_plot_widget.get_scrolling_plot_widget(),0,0)
    ml.addLayout(scrolling_plot_widget.get_scrolling_plot_layout(),0,0)
    mw.show()

    # Start Qt event loop unless running in interactive mode or using pyside
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
Run Code Online (Sandbox Code Playgroud)