最近在写串口通信时遇到了这一问题,需要从单片机读取数据并绘图。使用静态的绘图方法实现效果比较丑陋。而且随数据量的增大,坐标轴的比例会发生实时变化。使用滑动窗绘图可以很好地解决上述问题。
在这里,我定义下位机数据格式为
key1:value1 key2:value2 ... keyn:valuen
也就是说,最终的滑动窗可以绘制多个key的图像,横轴恒为时间,纵轴为不同的value。为了实现滑动窗的效果哟,可以使用队列。一旦绘制的数据量超过队列长度限制,就会舍弃之前的数据,再在接受新数据时设置回调函数触发绘图窗口的更新,就可以实现滑动窗的效果。
class Plotter(QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.par = Parameter()
self.startTime = 0
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.ax = self.figure.add_subplot(111)
self.colors = plt.cm.tab10(np.linspace(0, 1, 10)) # 使用 tab10 色板生成 10 种颜色
self.ax.set_prop_cycle(cycler('color', self.colors)) # 设置颜色循环
lay = QVBoxLayout()
lay.addWidget(self.canvas)
self.setLayout(lay)
def plotData(self):
self.ax.cla()
axTime = np.array(self.par.axTime)
for key, value in itertools.islice(self.par.axValue.items(), self.par.plotNum):
axValue = np.array(value)
self.ax.scatter(axTime, value, marker='.', label=f'{key}原始数据')
if len(axTime) > 3 and self.par.allowFit:
values_smooth = make_interp_spline(axTime, axValue, k=3) # 进行3次样条插值
self.ax.plot(axTime, values_smooth(axTime), label='拟合曲线')
self.ax.legend()
self.ax.set_title('数据可视化窗口')
self.ax.set_xlabel('t')
self.ax.set_ylabel('value')
self.canvas.draw()
数据处理部分代码如下:
def receiveData(self, data):
self.consoleShow.clear()
if self.par.isSaving:
try:
with open(self.par.saveFilename+'.pkl', 'ab') as f:
pickle.dump([time.time() - self.par.startSaveTime, data], f)
print(data)
except Exception as e:
self.par.signal.signalShowError.emit(string['DataSaveError'])
self.par.isSaving = False
self.ckSave.setChecked(False)
print(e)
if self.par.currentWidget == 'console':
if self.ckTime.isChecked():
data = time.strftime("%H:%M:%S -> ", time.localtime()) + data
if self.ckLine.isChecked():
data += '\n'
self.teReceiver.append(data)
elif self.par.currentWidget == 'plot':
try:
# 数据处理逻辑
data = data.split(' ')
dataDict = {}
for item in data:
dataDict[item.split(':')[0]] = float(item.split(':')[1]) # 确保数据为浮点型
addNewElementToQueue(self.par.axTime, self.par.dataMax, time.time() - self.plot.startTime)
for key, value in dataDict.items():
if key not in self.par.axValue.keys():
self.par.axValue[key] = deque(maxlen=self.par.dataMax)
addNewElementToQueue(self.par.axValue[key], self.par.dataMax, value)
self.consoleShow.append(key + ': ' + str(value) + '\n')
self.plot.plotData()
except ValueError:
self.par.signal.signalShowError.emit(string['PlotValueError'])
# 停止数据处理
except Exception as e:
print(e)
最终效果: