当前位置: 首页 > news >正文

用PyQtGraph+QTimer打造一个简易的传感器数据记录仪(附完整源码)

用PyQtGraph+QTimer构建高响应式传感器数据记录仪

最近在调试温室环境监测系统时,发现市面上的数据记录软件要么功能过剩,要么扩展性不足。于是我用PyQtGraph+QTimer搭建了一个轻量级解决方案,不仅能实时显示传感器数据,还能通过简单的拖拽查看历史曲线。这个方案特别适合需要快速验证传感器数据的创客和硬件开发者。

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(y=True) # 只显示Y轴网格 self.plot_widget.setLabel('left', '数值', '℃') # Y轴标签 self.plot_widget.setLabel('bottom', '时间') # X轴标签 # 关键性能优化设置 self.plot_widget.setDownsampling(mode='peak') # 峰值采样 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(pen=pg.mkPen(color='b', width=2)) # 更新函数 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(x=True, y=True) self.plot_widget.setLimits( xMin=0, xMax=10000, # 足够大的上限 yMin=0, yMax=50) # 根据传感器范围调整 # 添加十字光标 self.crosshair = pg.InfiniteLine(angle=90, movable=False) self.plot_widget.addItem(self.crosshair, ignoreBounds=True)

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 = f"sensor_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, port='COM3', baudrate=9600): self.serial = serial.Serial(port, baudrate, timeout=1) 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(color='r', width=2)) # 变红 else: self.curve.setPen(pg.mkPen(color='b', width=2)) # 恢复蓝色

数据统计分析

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())
http://www.jsqmd.com/news/542586/

相关文章:

  • Web应用集成实战:打造基于StructBERT的在线论文查重平台
  • Databricks社区版保姆级入门:从注册到第一个Spark分析(附避坑指南)
  • 如何快速提取图表数据:WebPlotDigitizer完整指南与3个高效技巧
  • 小白友好!Gemma-3-12B-IT WebUI部署常见错误及修复方法
  • 深度学习中的动态网络剪枝:从Dropout到Stochastic Depth的演进与实践
  • 从一次kubectl报错深入理解K8s高可用架构:Keepalived+HAProxy如何影响你的16443端口
  • 别再混淆了!微信小程序授权登录与手机号登录的完整流程对比(附SpringBoot后端代码)
  • WSL2下如何用微软雅黑替换文泉驿正黑字体(Debian/Ubuntu通用)
  • 三维旋转实战:用Python实现罗德里格旋转公式(附完整代码)
  • 告别NEDC!手把手教你将CLTC/WLTP等最新工况文件导入AVL Cruise(附资源包)
  • 学术研究助手:OpenClaw+nanobot实现文献关键信息提取
  • EVA-02模型快速入门:Anaconda虚拟环境配置与Python依赖安装
  • 实战指南:用nanomsg的六种通信模式(PAIR/REQREP/PUBSUB等)快速构建分布式微服务
  • 保姆级教程:在Ubuntu 20.04上为YOLOv11配置CUDA 12.8和PyTorch GPU环境(含常见驱动报错解决)
  • 避开网络坑!手把手教你用Anaconda在Windows上安装DeepLabCut 3.0(含CPU/GPU配置)
  • Cookie工具:开源Cookie管理与安全合规解决方案
  • AI科研方法论调研报告:人机协同时代的科研新范式
  • Realistic Vision V5.1 虚拟摄影棚数据科学应用:使用Matlab分析生成图像的色彩分布
  • Golang错误处理实战:defer、panic和recover的正确打开方式(附避坑指南)
  • 用字节扣子工作流,5分钟把小说变成AI动漫解说视频(附完整流程)
  • VScode+PlatformIO搭建Arduino开发环境全攻略(2024最新版)
  • 如何用A0模型提升机器人抓取效率?3D轨迹预测实战解析
  • LyricsX:突破平台限制,重构macOS歌词体验的开源解决方案
  • SDMatte多场景应用案例:人像发丝保留、素材精修、海报透明底批量生成
  • Python气象数据处理实战:用gma 2.0.8计算RMI指数(附Excel数据预处理技巧)
  • Visual Studio 2010实战:5分钟搞定Windows窗体学生管理系统(附完整源码)
  • OpenCore Legacy Patcher:三步让老旧Mac焕发新生,安装最新macOS系统
  • 安卓锁屏密码存储机制与安全攻防实战
  • LingBot-Depth部署避坑指南:常见问题与解决方案汇总
  • OFA-Image-Caption模型企业级部署架构设计:高可用与负载均衡方案