我正在尝试编写一个程序,通过串口从 arduino 获取串行数据,并实时绘制它。我使用 matplotlib 编写了代码,但我希望对结果感到满意,所以我试图让它在 pyqtgraph 上工作(学习如何使用它的资源要少得多)。我的问题是代码显示了一个空图。似乎 _update 仅被调用一次,但是当我将其放入循环中时,图表甚至不显示。
\n\n我编写了一些其他代码来实现我想要的功能,即实时绘制数据,在数据通过阈值后,它在数据上绘制新线,显示线性回归。我从这里得到了一个示例(https://github.com/JaFeKl/joystick_real_time_plot_with_pyqtgraph/blob/master/real_time_plot.py),因为我希望我的代码是可调用的(在函数中,但我无法让它工作.到目前为止,我正在从 python 中生成数据以简化调试
\n\nimport 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_())\nRun Code Online (Sandbox Code Playgroud)\n\n我期望结果与此代码类似(仅没有线性回归)
\n\nimport 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##################\nRun Code Online (Sandbox Code Playgroud)\n
我没有连接 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)