用PyQtGraph+QTimer打造一个简易的传感器数据记录仪(附完整源码)
用PyQtGraphQTimer构建高响应式传感器数据记录仪最近在调试温室环境监测系统时发现市面上的数据记录软件要么功能过剩要么扩展性不足。于是我用PyQtGraphQTimer搭建了一个轻量级解决方案不仅能实时显示传感器数据还能通过简单的拖拽查看历史曲线。这个方案特别适合需要快速验证传感器数据的创客和硬件开发者。1. 环境搭建与核心组件选型在开始编码前我们需要明确几个关键技术选择。PyQtGraph作为Qt的绘图库相比Matplotlib有着更好的实时性能而QTimer则是实现定时采样的核心组件。安装基础环境只需两行命令pip install pyqtgraph pyserial关键组件分工PyQtGraph负责高效渲染实时曲线QTimer实现固定间隔的数据采样PyQt5构建完整的GUI界面PySerial可选连接真实硬件传感器提示建议使用Python 3.8环境某些旧版本可能存在库兼容性问题2. 界面布局设计与绘图区域配置我们先从界面设计开始。使用Qt Designer可以快速搭建UI但为了更好的可定制性这里采用纯代码方式构建。核心布局结构如下import pyqtgraph as pg from PyQt5 import QtWidgets, QtCore class SensorMonitor(QtWidgets.QWidget): def __init__(self): super().__init__() # 主布局 self.main_layout QtWidgets.QVBoxLayout() self.setLayout(self.main_layout) # 绘图区域 self.plot_widget pg.PlotWidget() self.main_layout.addWidget(self.plot_widget) # 控制面板 self.control_panel QtWidgets.QHBoxLayout() self.main_layout.addLayout(self.control_panel)绘图区域需要特别优化配置# 配置绘图参数 self.plot_widget.setBackground(w) # 白色背景 self.plot_widget.showGrid(yTrue) # 只显示Y轴网格 self.plot_widget.setLabel(left, 数值, ℃) # Y轴标签 self.plot_widget.setLabel(bottom, 时间) # X轴标签 # 关键性能优化设置 self.plot_widget.setDownsampling(modepeak) # 峰值采样 self.plot_widget.setClipToView(True) # 只渲染可见区域3. 数据采集与实时绘图实现数据采集系统需要处理两个核心问题定时采样和数据缓冲管理。我们使用双缓冲策略来平衡实时性和历史数据查看需求。3.1 定时采样机制# 初始化数据容器 self.data_buffer [] self.time_stamps [] self.max_points 1000 # 缓冲区大小 # 创建定时器 self.timer QtCore.QTimer() self.timer.timeout.connect(self.update_plot) self.timer.start(100) # 10Hz采样率 # 模拟传感器数据生成 def generate_sensor_data(self): import random return random.uniform(20, 30) # 模拟20-30℃温度数据3.2 实时绘图优化技巧常见的性能陷阱是每次重绘整个曲线。正确的做法是预先创建曲线对象然后只更新数据# 初始化时创建曲线 self.curve self.plot_widget.plot(penpg.mkPen(colorb, width2)) # 更新函数 def update_plot(self): new_value self.generate_sensor_data() # 管理数据缓冲区 if len(self.data_buffer) self.max_points: self.data_buffer.pop(0) self.time_stamps.pop(0) self.data_buffer.append(new_value) self.time_stamps.append(len(self.time_stamps)) # 高效更新曲线 self.curve.setData(self.time_stamps, self.data_buffer) # 自动滚动视图 if len(self.time_stamps) 10: self.plot_widget.setXRange( max(0, len(self.time_stamps)-20), len(self.time_stamps)5)4. 历史数据查看与导出功能实现历史数据浏览需要解决两个问题视图平移控制和数据持久化。4.1 视图交互配置# 启用平移和缩放 self.plot_widget.setMouseEnabled(xTrue, yTrue) self.plot_widget.setLimits( xMin0, xMax10000, # 足够大的上限 yMin0, yMax50) # 根据传感器范围调整 # 添加十字光标 self.crosshair pg.InfiniteLine(angle90, movableFalse) self.plot_widget.addItem(self.crosshair, ignoreBoundsTrue)4.2 数据导出实现添加导出按钮和功能# 在控制面板添加导出按钮 self.export_btn QtWidgets.QPushButton(导出CSV) self.control_panel.addWidget(self.export_btn) self.export_btn.clicked.connect(self.export_data) def export_data(self): from datetime import datetime import csv filename fsensor_data_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv with open(filename, w, newline) as f: writer csv.writer(f) writer.writerow([时间戳, 传感器值]) for ts, val in zip(self.time_stamps, self.data_buffer): writer.writerow([ts, val])5. 连接真实硬件传感器对于使用真实传感器如Arduino的情况我们需要添加串口通信模块import serial class HardwareSensor: def __init__(self, portCOM3, baudrate9600): self.serial serial.Serial(port, baudrate, timeout1) def read_value(self): line self.serial.readline().decode(ascii).strip() try: return float(line) except ValueError: return None # 在主类中初始化 self.sensor HardwareSensor() # 修改update_plot方法 def update_plot(self): new_value self.sensor.read_value() if new_value is not None: # 其余逻辑保持不变...6. 高级功能扩展基础功能实现后可以考虑添加这些实用功能多通道支持为每个传感器通道创建独立的曲线对象使用不同颜色区分通道添加图例说明报警阈值设置# 在控制面板添加阈值设置 self.threshold_spin QtWidgets.QDoubleSpinBox() self.threshold_spin.setRange(0, 100) self.threshold_spin.setValue(28) # 默认阈值 self.control_panel.addWidget(QtWidgets.QLabel(报警阈值:)) self.control_panel.addWidget(self.threshold_spin) # 在update_plot中添加检查 if new_value self.threshold_spin.value(): self.curve.setPen(pg.mkPen(colorr, width2)) # 变红 else: self.curve.setPen(pg.mkPen(colorb, width2)) # 恢复蓝色数据统计分析def show_stats(self): import numpy as np data np.array(self.data_buffer) stats f 当前值: {data[-1]:.2f} 平均值: {np.mean(data):.2f} 最大值: {np.max(data):.2f} 最小值: {np.min(data):.2f} 标准差: {np.std(data):.2f} QtWidgets.QMessageBox.information(self, 统计数据, stats.strip())

相关新闻

最新新闻

日新闻

周新闻

月新闻