PyQt5 + pyserial + matplotlib实现串口数据实时绘图及保存应用开发教程
串口数据实时绘图应用开发教程
1. 简介
在本教程中,我们将使用Python和PyQt库来开发一个简单的串口数据实时绘图应用。该应用能够实时接收串口数据并绘制成动态曲线,同时保存数据到本地CSV文件。
2. 开发环境搭建
首先,确保你已经在Windows上安装了Python。接着,通过以下命令安装必要的库:
pip install PyQt5 pyserial matplotlib
3. 代码分析
让我们一步步来分析这个应用的代码:
3.1 创建PyQt窗口
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QLabel, QPushButton, QComboBox, QWidget
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
from serial.tools import list_ports
import serial
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import csv
from datetime import datetime
from matplotlib.ticker import AutoLocator, AutoMinorLocator
class SerialThread(QThread):
data_received = pyqtSignal(str)
def __init__(self, serial_port, parent=None):
super(SerialThread, self).__init__(parent)
self.serial_port = serial_port
def run(self):
while self.serial_port.isOpen():
try:
data = self.serial_port.readline().decode().strip()
if data:
self.data_received.emit(data)
except ValueError as e:
print(f"Error converting data to float: {e}")
class SerialPlotter(QMainWindow):
def __init__(self):
super().__init__()
# ...(省略其他初始化代码)
self.setup_ui()
def setup_ui(self):
central_widget = QWidget(self)
layout = QVBoxLayout(central_widget)
# 添加串口选择、参数设置和按钮等控件
# ...
self.plot_canvas = FigureCanvas(plt.Figure())
# 添加控件到布局
# ...
self.setCentralWidget(central_widget)
在这一部分,我们创建了一个继承自QMainWindow
的主窗口类SerialPlotter
,以及一个继承自QThread
的串口通信线程类SerialThread
。SerialPlotter
类中的setup_ui
方法用于创建界面,包括串口选择、参数设置和实时绘图区域。
3.2 配置串口参数
def refresh_serial_ports(self):
# 更新可用串口列表
port_list = [p.device for p in list_ports.comports()]
self.serial_combo.clear()
self.serial_combo.addItems(port_list)
def toggle_serial(self):
if self.serial_open:
self.stop_serial()
else:
self.start_serial()
def start_serial(self):
port_name = self.serial_combo.currentText()
self.baud_rate = int(self.baud_combo.currentText())
data_bits_text = self.data_bits_combo.currentText()
self.data_bits = serial.EIGHTBITS if data_bits_text == "8" else \
serial.SEVENBITS if data_bits_text == "7" else serial.EIGHTBITS
stop_bits_text = self.stop_bits_combo.currentText()
self.stop_bits = serial.STOPBITS_ONE if stop_bits_text == "1" else \
serial.STOPBITS_ONE_POINT_FIVE if stop_bits_text == "1.5" else \
serial.STOPBITS_TWO if stop_bits_text == "2" else serial.STOPBITS_ONE
try:
self.serial_port = serial.Serial(port=port_name, baudrate=self.baud_rate, timeout=0.1)
self.serial_port.bytesize = self.data_bits
self.serial_port.stopbits = self.stop_bits
self.serial_thread = SerialThread(self.serial_port)
self.serial_thread.data_received.connect(self.handle_data_received)
self.serial_thread.start()
self.timer.start(1000)
self.start_button.setText("停止接收")
self.serial_open = True
except serial.SerialException as e:
print(f"Error opening serial port: {e}")
在这一部分,我们定义了一些方法用于刷新可用串口列表和控制串口的开启和关闭。start_serial
方法中,我们获取了用户在界面上选择的串口和参数,然后通过serial
库打开串口,并创建一个用于串口通信的线程。
3.3 创建串口通信线程
class SerialThread(QThread):
data_received = pyqtSignal(str)
def __init__(self, serial_port, parent=None):
super(SerialThread, self).__init__(parent)
self.serial_port = serial_port
def run(self):
while self.serial_port.isOpen():
try:
data = self.serial_port.readline().decode().strip()
if data:
self.data_received.emit(data)
except ValueError as e:
print(f"Error converting data to float: {e}")
在SerialThread
类中,我们重载了run
方法,该方法会在线程启动后执行。在这个方法中,我们使用一个无限循环来实时接收串口数据,并通过pyqtSignal
发送数据到主线程。
3.4 实时绘图和数据保存
def update_plot(self):
if self.timestamps and self.data:
ax = self.plot_canvas.figure.add_subplot(111)
ax.clear()
ax.plot(self.timestamps, self.data, marker='o')
ax.set_xlabel('时间')
ax.set_ylabel('数据值')
ax.xaxis.set_major_locator(AutoLocator())
ax.yaxis.set_major_locator(AutoLocator())
ax.xaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_locator(AutoMinorLocator())
self.plot_canvas.draw()
self.save_to_csv()
def save_to_csv(self):
with open('serial_data.csv', 'a', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow([self.timestamps[-1], self.data[-1]])
update_plot
方法用于更新Matplotlib图表。在这个方法中,我们使用AutoLocator
和AutoMinorLocator
自动调整横纵坐标的精度。同时,我们调用save_to_csv
方法将最新接收到的数据保存到CSV文件中。
3.5 整体功能
if __name__ == '__main__':
app = QApplication(sys.argv)
window = SerialPlotter()
window.show()
sys.exit(app.exec_())
在主程序中,我们创建了一个QApplication
实例,实例化了SerialPlotter
窗口,并通过show
方法显示窗口。最后,通过sys.exit(app.exec_())
来确保程序在关闭窗口时能够正常退出。
4. 整体功能
这个应用的整体功能是通过一个简洁的界面实时绘制串口数据。用户可以选择不同的串口和参数,点击“开始接收”按钮后,应用将实时接收串口数据并在界面上绘制成动态曲线。同时,接收到的数据将以CSV格式保存到本地文件。
5. 程序使用方法
- 运行Python脚本。
- 在界面上选择串口、波特率、数据位和停止位。
- 点击“开始接收”按钮。
- 查看动态绘制的曲线。
- 点击“停止接收”按钮以停止接收数据。
- 数据保存在
serial_data.csv
文件中。
界面效果:
数据保存效果:
通过这个实例,我们了解了如何使用PyQt、serial和Matplotlib库开发一个串口数据实时绘图应用。这个项目结合了串口通信、GUI开发和数据可视化的知识点,是一个不错的学习材料。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!