即使设备断开连接,使用PyQtGraph的实时绘图也在绘制

时间:2016-05-04 12:51:17

标签: python plot pyserial pyqtgraph

我使用Pyserial和PyQtgraph绘制实时数据。我正在从(一个arduino)和我的电脑读取数据的设备之间的连接工作正常,我的意思是我可以读取数据。问题在于,当我断开设备时,数据仍在绘图中。并且,如果我让它继续阅读,过了一会儿,情节会崩溃,我必须重新开始。

我正在阅读一些帖子,我发现了这个:

implementing pyqtgraph for live data graphing

所以,我认为问题在于,在我的代码中,数据附加到列表然后是绘图,这使得它变慢,也许这就是它崩溃的原因。

这是我的代码:

class MyApplication(QtGui.QApplication):
  def __init__(self, *args, **kwargs):
    super(MyApplication, self).__init__(*args, **kwargs)
    self.t = QTime()
    self.t.start()

    self.data = deque()

    self.cnt = 0 

    self.win = pg.GraphicsWindow()

    self.plot = self.win.addPlot(title='Timed data')
    self.curve = self.plot.plot()

    self.tmr = QTimer()
    self.tmr.timeout.connect(self.update)
    self.tmr.start(100)

    self.cnt = 0

    print "Opening port"
    self.raw=serial.Serial("com4",9600)
    print "Port is open"

  def update(self):
    line = self.raw.read()
    ardString = map(ord, line)
    for number in ardString:
        numb = float(number/77.57)
        self.cnt += 1
        x = self.cnt/20

        self.data.append({'x': x , 'y': numb}) 
        x = [item['x'] for item in self.data]
        y = [item['y'] for item in self.data]
        self.curve.setData(x=x, y=y)

如何修改我的代码以使用上面帖子中编写的代码?或者我如何绘制即将发布的数据而不将其附加到列表中?

抱歉,我是PyQtGraph的新人,现在我很困惑。希望您能够帮助我。

----------编辑---------

我尝试过这样一个更简单的代码:

import serial
import numpy
import matplotlib.pyplot as plt

print "Opening port"
port = "com4"
arduinoData = serial.Serial(port, 9600)

while True:
  if arduinoData.inWaiting()>0:
    print "Reading data"
    arduinoString = arduinoData.read(arduinoData.inWaiting())

    bytes = map(ord, arduinoString)

    for byte in bytes:
        print byte
  else:
    print "There is no data"

因此,在命令提示符中显示数据后,我断开设备,我可以看到数据仍然显示几秒钟。然后,出现“没有数据”文本。那么,可能是什么问题呢?我知道,它是缓冲数据,但在我看来,它与其他代码发生的情况相同。

----------编辑2 ---------

我终于完成了我需要做的事情。感谢@busfault的所有帮助和耐心。

这是update方法的代码:

def update(self): 
    line = self.raw.read([1])                        
    ardString = map(ord, line)                        

    for number in ardString:                          
        numb = float(number/77.57)                    

        self.data.append(numb)                        
        self.yData.append(numb)                       

        if len (self.yData)>300 :
            self.yData = []
            self.raw.flush()

    self.curve.setData(self.yData)

我现在所做的是数据分为两个不同的列表:self.yDataself.data。 在self.yData中,我最多只能附加300个数据项(这是随机的,我本可以选择500个),然后我清空所有数据并“清除”列表再重新开始。

Whit我可以毫不拖延地查看实时数据,并将所有数据保存在另一个安全的地方。

2 个答案:

答案 0 :(得分:1)

我认为如果你创建列表你应该看到加速,如果你设置使用deque,那么我建议移动for循环之外的x和y列表的生成,因为那可能是你可能没有必要花很多时间。

def __init__(self, *args, **kwargs):
    super(MyApplication, self).__init__(*args, **kwargs)
    self.t = QTime()
    self.t.start()

    #self.data = deque()
    self.xValues = []
    self.yValues = []


    self.cnt = 0 

    self.win = pg.GraphicsWindow()

    self.plot = self.win.addPlot(title='Timed data')
    self.curve = self.plot.plot()

    self.tmr = QTimer()
    self.tmr.timeout.connect(self.update)
    self.tmr.start(100)

    self.cnt = 0

    print "Opening port"
    ##EDIT CHANGED THIS LINE TO INCLUDE TIMEOUT
    self.raw=serial.Serial("com4",9600, timeout=0)
    print "Port is open"

  def update(self):
    line = self.raw.read()
    ardString = map(ord, line)
    for number in ardString:
        numb = float(number/77.57)
        self.cnt += 1
        x = self.cnt/20

        self.xValues.append(x)
        self.yValues.append(numb)
        #self.data.append({'x': x , 'y': numb}) 
    #x = [item['x'] for item in self.data]
    #y = [item['y'] for item in self.data]
    self.curve.setData(x=x, y=y)

来自PySerial文档: https://pythonhosted.org/pyserial/pyserial_api.html#serial.Serial.read

  

从串口读取大小字节。如果设置了超时,则可能会根据请求返回较少的字符。没有超时,它将阻塞,直到读取所请求的字节数。

并从构造函数

  

控制read()行为的参数超时的可能值:

     
      
  • timeout =无:永远等待/直到收到请求的字节数
  •   
  • timeout = 0:非阻塞模式,在任何情况下立即返回,返回零或更多,直到请求的字节数
  •   
  • timeout = x:设置超时为x秒(允许浮点数),当请求的字节数可用时立即返回,否则等到超时到期并返回之前收到的所有字节。
  •   

因此默认情况下(timeout=None),当执行self.raw.read()并且没有数据时,它会尝试读取一个字节,然后永远等待。对于要写入的字节。

==================================

我正在考虑更多关于断开连接后代码崩溃的原因。我想我知道为什么,self.tmr每隔100毫秒就会生成一次信号,你的插槽(更新)会一遍又一遍地被调用,而且,self.raw.read()一直被调用(我想?)

尝试更改update()中的代码:

def update(self):
    self.tmr.stop()#Prevent the timer from entering again.
    line = self.raw.read()
    ardString = map(ord, line)
    for number in ardString:
        numb = float(number/77.57)
        self.cnt += 1
        x = self.cnt/20

        self.xValues.append(x)
        self.yValues.append(numb)
        #self.data.append({'x': x , 'y': numb}) 
    #x = [item['x'] for item in self.data]
    #y = [item['y'] for item in self.data]
    self.curve.setData(x=x, y=y)
    self.tmr.start()#restart the timer (resets the timeout)

我不知道保持100毫秒脉冲是否至关重要?如果是这样,您可以使用锁定,以便在再次调用更新时,它不会再次运行相同的代码。 https://docs.python.org/2/library/threading.html#rlock-objects

我认为这个例子表明它很容易实现。

import threading

some_rlock = threading.RLock()

with some_rlock:
    print "some_rlock is locked while this executes"

答案 1 :(得分:0)

我可以建议如下:将数据上传到db(数据库)。

这会涉及到为现有程序添加更多代码=)

但这可以使用任何db:sqlight,couchdb,mongodb..etc等轻松实现

或者只是创建一个跟踪已处理值的文件? 我注意到你没有使用列表,而是使用元组来存储你的密钥:值对。

    self.data.append({'x': x , 'y': numb}) 
    x = [item['x'] for item in self.data]
    y = [item['y'] for item in self.data]
    self.curve.setData(x=x, y=y)

因此对于第二个选项

tracking_file = open("filename.txt", "w") #w indicates write
tracking_file.writelines(the data) #instead of appending to a tuple or list
track_file.close()

此序列打开一个文件并将数据写入其中,以获取有关输入/输出的更多信息https://docs.python.org/2/tutorial/inputoutput.html

随后,您可以从您创建的文件中读取数据,如果程序崩溃,则不会删除文件,并且您可以通过在读取模式下打开文件,从崩溃或断开连接之前的程序中恢复,检查最后一个输入并继续添加值...