我正在使用 Qt(当前为 PyQt)在 Python 中编写一个应用程序,该应用程序通过 UDP 接收遥测(位置数据)并显示该数据。它可以处理几种数据包类型,每种类型都有几个数据字段。我有一个单独的线程来处理接收和解码数据包,它们会发出信号将该数据发送到 Qt GUI 进行显示。数据以多种方式显示:当前值显示、滚动日志显示和 3D 视图显示。所有这一切都运行得很好,并且每个数据包都以高达约 30Hz 的频率实时更新。
我想添加另一个显示实时图的选项卡。用户可以选择要绘制的数据流,它将实时更新,显示最后 60 秒数据的滑动窗口(可能在某些时候可配置)。但是我的第一种方法特别慢。它几乎没有跟上我们的模拟器只绘制一条运行速度远低于 30Hz 的线。
我正在使用 pyqtgraph 进行绘图。我在我的 .ui 文件中实例化了一个 PlotWidget,并为可以在绘图上绘制的每条数据线创建一个 PlotDataItem。我正在使用双端队列来存储要绘制的数据,包括值和时间。这样,我可以在数据进入时快速添加数据,并在数据超出滑动窗口时将其删除。我将所有这些存储在每个数据包类型和字段的字典中:
self.plotData[pktType][field] = {}
self.plotData[pktType][field]['curve'] = self.pwPlot.plot()
self.plotData[pktType][field]['starttime'] = time
self.plotData[pktType][field]['data'] = coll.deque()
self.plotData[pktType][field]['realtime'] = coll.deque()
self.plotData[pktType][field]['time'] = coll.deque()
'starttime' 存储用于计算经过的秒数的初始日期时间值。'realtime' 存储收到每个数据包的日期时间值(我目前没有使用它,所以如果可以节省时间,我可以放弃它)。'time' 存储从 'starttime' 开始经过的秒数,以便于绘图,'data' 存储值。
当一个数据包进来时,我将数据存储在我可能想要解析的每个字段的双端队列中。然后我修剪掉滑动窗口之外的所有数据。最后,双端队列被打包在一个 numpy 数组中并传递给 PlotDataItem 的 setData 方法。这是为每个接收到的数据包运行的代码的简化版本:
def updatePlot(self, time, pktData):
pktType = pktData['ptype']
keys = self.getPlottableFromType(pktType) # list of fields that can be plotted
if keys == None:
return
for k in keys:
self.plotData[pktType][k]['data'].append(pktData[k])
self.plotData[pktType][k]['realtime'].append(time)
runtime = (time - self.plotData[pktType][k]['starttime']).total_seconds()
if self.plotRate == 0:
self.plotData[pktType][k]['time'].append(runtime)
else:
if self.plotData[pktType][k]['time']: # has items
nexttime = self.plotData[pktType][k]['time'][-1] + 1. / self.plotRate
else:
nexttime = 0
self.plotData[pktType][k]['time'].append(nexttime)
while (self.plotData[pktType][k]['time'][-1] - self.plotData[pktType][k]['time'][0]) > self.plotRangeSec:
self.plotData[pktType][k]['data'].popleft()
self.plotData[pktType][k]['realtime'].popleft()
self.plotData[pktType][k]['time'].popleft()
self.drawPlot(pktType, k)
def drawPlot(self, pktType, k):
if self.plotIsEnabled(pktType, k) and self.plotData[pktType][k]['time']: # has items
npt = np.array(self.plotData[pktType][k]['time'])
npd = np.array(self.plotData[pktType][k]['data'])
self.plotData[pktType][k]['curve'].setData(npt, npd)
else:
self.plotData[pktType][k]['curve'].clear()
self.plotRate 可用于绘制数据,使用墙上时间或强制时间轴为固定更新率。这对于与模拟器一起使用很有用,因为它比真实系统运行得慢。
我可能应该做的第一件事不是每次都为没有绘制的图调用 .clear() (只是使逻辑复杂一点)。除此之外,有人有任何提高性能的技巧吗?numpy 数组的双端队列是一个好策略吗?有没有更好的方法来更新数据,因为我每行只更改几个值(添加一个点并可能删除一两个点)?