class 中的动画 pyqtgraph

Animate pyqtgraph in class

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

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

import sys
import pyqtgraph as pg
import pyqtgraph.exporters
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np

import serial

# test
import math
import time


class Graph(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Graph, self).__init__(parent)
        self.n = 3
        self.mainbox = QtGui.QWidget()
        self.setCentralWidget(self.mainbox)
        self.mainbox.setLayout(QtGui.QVBoxLayout())

        self.canvas = pg.GraphicsLayoutWidget()             # create GrpahicsLayoutWidget obejct  
        self.mainbox.layout().addWidget(self.canvas)

        #  Set up plot
        self.analogPlot = self.canvas.addPlot(title='Signal from serial port')
        self.analogPlot.setYRange(-1,1123)                # set axis range
        self.analogPlot.setXRange(-1,1123)
        self.analogPlot.showGrid(x=True, y=True, alpha=0.5) # show Grid
        x_axis = self.analogPlot.getAxis('bottom')
        y_axis = self.analogPlot.getAxis('left')
        font=QtGui.QFont()
        font.setPixelSize(20)
        x_axis.tickFont = font
        y_axis.tickFont = font
        x_axis.setLabel(text='Tensão [V]')              # set axis labels
        y_axis.setLabel(text='Corrente [mA]')

        self.plts = []
        self.intplts = []
        colors = ['r', 'b', 'w', 'y', 'g', 'm', 'c', 'k']
        for i in range(self.n):
            self.plts.append([])
            self.intplts.append([])


        for i in range(self.n):
            if len(self.plts) <= len(colors):
                self.plts[i]=(self.analogPlot.plot(pen= pg.mkPen(colors[i], width=6)))
        for i in range(self.n):
            if len(self.plts) <= len(colors)*2:
                self.intplts.append(self.analogPlot.plot(pen= pg.mkPen(colors[i+3], width=3)))

        #Data
        self.datay = []
        self.datax = []
        for i in range(self.n):
            self.datax.append([])
            self.datay.append([])

        # set up image exporter (necessary to be able to export images)
        QtGui.QApplication.processEvents()
        self.exporter=pg.exporters.ImageExporter(self.canvas.scene())
        self.image_counter = 1


        # start updating
        self.t=0

        self._update()


    def _update(self):
        time.sleep(0.01)
        if self.t<= 30:
            #line = raw.readline()
            #data.append(int(line))
            self.datay[0].append(math.sin(self.t+(math.pi/2)))
            self.datay[1].append(math.sin(self.t+(5*math.pi/4)))
            self.datay[2].append(math.sin(self.t))
            self.datax[0].append(self.t)
            self.datax[1].append(self.t)
            self.datax[2].append(self.t)
            self.t+=0.1
            self.plts[0].setData(self.datax[0], self.datay[0])
            self.plts[1].setData(self.datax[1], self.datay[1])
            self.plts[2].setData(self.datax[2], self.datay[2])

            app.processEvents()
        elif self.t>=30 and self.t<=30.1 :
            self.t+=1

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    plot = Graph()
    plot.show()
    sys.exit(app.exec_())

我希望得到类似于此代码的结果(只是没有线性回归)

import pyqtgraph as pg
import pyqtgraph.exporters
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np

# linear regression
from scipy import stats

#Arduino
#import find_arduino
#import find_buad
import serial

import math
import time

#port = find_arduino.FindArduino()
#baud = find_buad.FindBaudRate()
ard=None

def Con():
    global ard
    ard = serial.Serial(port,baud,timeout=5)
    time.sleep(2) # wait for Arduino
    ard.close()

# define the data
theTitle = "pyqtgraph plot"
datay = [[],[],[]]
datax = [[],[],[]]
x2 = []
T=[]
t=0

y1L=[]
x1L=[]

# create plot
### START QtApp #####
app = QtGui.QApplication([])            # you MUST do this once (initialize things)
####################
win = pg.GraphicsWindow(title="Signal from serial port") # creates a window
plt = win.addPlot(title="Realtime plot")  # creates empty space for the plot in the window

font=QtGui.QFont()
font.setPixelSize(20)
plt.getAxis("bottom").tickFont = font
plt.getAxis("left").tickFont = font

plt1 = plt.plot(pen=pg.mkPen('r', width=6))
plt2= plt.plot(pen=pg.mkPen('b', width=6))
plt3= plt.plot(pen=pg.mkPen('w', width=6))

plt1I = plt.plot(pen=pg.mkPen('y', width=3))
plt2I = plt.plot(pen=pg.mkPen('g', width=3))
plt3I = plt.plot(pen=pg.mkPen('m', width=3))

plt.showGrid(x=True,y=True)


def update():
    global plt1,plt2,plt3, t, plt1I, plt2I, plt3I
    if t<= 30:
        #line = raw.readline()
        #data.append(int(line))
        datay[0].append(math.sin(t+(math.pi/2)))
        datay[1].append(math.sin(t+(5*math.pi/4)))
        datay[2].append(math.sin(t))
        datax[0].append(t)
        datax[1].append(t)
        datax[2].append(t)
        t+=0.1
        plt1.setData(datax[0],datay[0])
        plt2.setData(datax[1],datay[1])
        plt3.setData(datax[2],datay[2])
        app.processEvents()
        time.sleep(0.01)
    elif t>=30 and t<=30.1 :
        #plt1I.setData([0,1,2],[5,3,1])
        #app.processEvents()
        interp(plt1I, plt2I, plt3I)
        t+=1
    else:
        app.processEvents()

def interp(pt1, pt2, pt3):
    slope, intercept, r_value, p_value, std_err = stats.linregress(datax[0][10:],datay[0][10:])
    x=[]
    y=[]
    print(slope)
    for i in datax[0][10:]:
        x.append(i)
        y.append(intercept+slope*i)
    pt1.setData(x,y)

    slope, intercept, r_value, p_value, std_err = stats.linregress(datax[1][10:],datay[1][10:])
    x=[]
    y=[]
    print(slope)
    for i in datax[0][10:]:
        x.append(i)
        y.append(intercept+slope*i)
    pt2.setData(x, y)

    slope, intercept, r_value, p_value, std_err = stats.linregress(datax[2][10:],datay[2][10:])
    x=[]
    y=[]
    print(slope)
    for i in datax[0][10:]:
        x.append(i)
        y.append(intercept+slope*i)
    pt3.setData(x,y)
    app.processEvents()

timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

### MAIN PROGRAM #####    
# this is a brutal infinite loop calling your realtime data plot
# make this interpret the incoming data
#Con()
#Communicate(1)
while True: update()


### END QtApp ####
pg.QtGui.QApplication.exec_() # you MUST put this at the end
##################

我没有连接 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_()