即使设备断开连接,使用 PyQtGraph 的实时绘图仍在绘制
Posted
技术标签:
【中文标题】即使设备断开连接,使用 PyQtGraph 的实时绘图仍在绘制【英文标题】:Live plot using PyQtGraph is drawing even with the device disconnected 【发布时间】:2016-08-29 22:19:26 【问题描述】:我正在使用 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.yData
和 self.data
。
在self.yData
中,我最多只能附加 300 个数据项(这是随机的,我可以选择 500 个),然后我刷新所有数据并“清除”列表以重新开始。
有了这个,我可以立即查看实时数据并将它们全部保存在另一个安全的地方。
【问题讨论】:
你需要显示整个数据集吗? 您好,感谢您的回复。是的,我需要整个数据集 代码到底在哪里崩溃了?我假设它是在setData
通话期间?另外,您是否需要按原样存储数据?您可以轻松地添加一个 x
和一个 y
列表,以及附加的列表。我认为您可能希望将行 x = [item['x'] for item in self.data]
、y = [item['y'] for item in self.data]
和 self.curve.setData(x=x, y=y)
移出一层,这样它们就不会在循环期间每次都被调用,并且只有在循环处理 ardString
【参考方案1】:
我认为,如果您在创建列表的过程中会看到速度加快,如果您设置使用双端队列,那么我建议将 x 和 y 列表的生成移到 for 循环之外,因为这可能是你在不必要的时候花费了很多时间。
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
从串口读取 size 个字节。如果设置了超时,它可能会根据请求返回更少的字符。没有超时,它将阻塞,直到读取请求的字节数。
来自构造函数
控制 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 毫秒脉冲是否很重要?如果是这样,您可以使用 Lock 以便在再次调用 update 时不会再次运行相同的代码。 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"
【讨论】:
您好,感谢您的回答。我以前试过这个,但我还是一样:断开设备后它一直在画。 我想我现在可能对正在发生的事情有了更好的想法。您应该检查更新是否超出 line = self.raw.read() 您可以添加的另一件事是一次读取多个字节,而不是一次读取一个。串行线路传输的频率和数据量是多少?您可能需要/想要提高 9600 波特率。 抱歉回复晚了。我使用的波特率为 115200 波特。我明白你的意思,但我不知道如何读取多个字节。并感谢您的所有帮助。 我成功了!!! .感谢您的所有帮助和耐心。看上面的“Edit2”。再次感谢您。【参考方案2】:我可以建议以下几点:将数据上传到 db(数据库)。
这将涉及向现有程序添加更多代码 =)
但这很容易使用任何数据库实现:sqllight、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
随后,您可以从您创建的文件中读取数据,如果您的程序崩溃,这些文件将不会被删除,并且您可以通过以读取模式打开文件,从程序崩溃或断开连接之前离开的位置恢复, 检查最后一个输入并继续添加值...
【讨论】:
我建议将文件打开样式更改为with open("filename.txt", 'w') as tracking_file:
另请注意,使用“w”会覆盖文件,您可以将其更改为with open("filename.txt", 'a') as tracking_file:
,然后只写入当前值。如果您每次都调用它,那么您将有一个增量时间来写入文件(为什么每次都写入所有数据?)
您好,谢谢您的回答。是的,这是我有时遇到的崩溃的解决方案,谢谢。但我需要做的是尽可能快地提取我从设备接收到的数据。
而不是追加两次(2个不同的列表,一个用于x,另一个用于y),您可以将数据作为一个整体写入。使用数据库,将数据作为字典、x:y 键值和/或文件上传。此外,使用 key:value 检索数据,这将优化程序的编写速度。我相信最初,您发布的问题是您的程序崩溃了,您必须重新启动。请考虑重新编写您的原始帖子,以便其他用途可以提供准确的解决方案
要优化性能,识别程序中的瓶颈并检查优化非常重要。您可能需要考虑阅读:wiki.python.org/moin/PythonSpeed/PerformanceTips 您可以寻找一个装饰器来为您的方法计时并尝试对其进行优化。以上是关于即使设备断开连接,使用 PyQtGraph 的实时绘图仍在绘制的主要内容,如果未能解决你的问题,请参考以下文章