如何在 Python 程序中为一个简单的 UI 显示实时图形?
Python 是一门非常流行的编程语言,因为它易于学习、易于使用和易于阅读。在数据可视化方面,Python 也提供了很多库,例如 Matplotlib、Bokeh 和 Plotly 等。这些库能够帮助我们轻松地进行数据可视化,但有时候我们需要实时地显示一些图形,比如说心电图(ECG)或者温度曲线等等。在本文中,我们将会介绍如何使用 Python 和 matplotlib 来实时地显示图形。
更多Python文章,请阅读:Python 教程
简介
Matplotlib 是一个用于绘制 2D 和 3D 图形的 Python 库。它是 Python 数据可视化的主要工具之一,支持从简单的线图到复杂的爆炸图的各种绘图类型。Matplotlib 的一个有用的特性是它的可交互性,这使得它成为一个很好的实时数据可视化工具。当然,它也可以制作静态图形。
前置条件
在本文中,我们将使用 Python3 和 matplotlib 库。在开始之前,请确保您已经安装了最新版本的 matplotlib。可以执行以下代码检查是否安装了 matplotlib:
import matplotlib
print(matplotlib.__version__)
应该输出 matplotlib 的版本号,如果输出为空,则说明未安装该库。在这种情况下,请执行以下安装命令:
pip install matplotlib
示例
首先,我们将创建一个简单的 GUI 窗口,显示一个名为“Real-time Plot”的标题和一个 matplotlib 绘图区域。我们可以使用 QtDesigner 工具或 PyQt5 的代码来创建 GUI 窗口,该代码与这篇文章不直接相关,因此在此省略。
在本文中,我们将展示如何创建一个 GUI 窗口,它包含一个菜单栏、工具栏和一个 matplotlib 绘图区域。第一个工具栏按钮将用于停止/暂停实时绘图,第二个按钮将用于清除当前绘图,第三个按钮将用于退出应用程序。代码如下所示:
import sys
import numpy as np
from PyQt5 import QtWidgets, QtGui, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class RealTimePlot(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(RealTimePlot, self).__init__(parent)
self.initUi()
def initUi(self):
# 创建一个matplotlib图形
self.figure = Figure()
# 为GUI设置一个标题
self.setWindowTitle('Real-time Plot')
# 为GUI设置一个菜单栏
menubar = self.menuBar()
file_menu = menubar.addMenu('File')
exit_action = file_menu.addAction('Exit')
exit_action.triggered.connect(self.close)
# 为GUI设置一个工具栏
toolbar = self.addToolBar('Tool')
pause_action = QtWidgets.QAction(QtGui.QIcon('icons/pause.png'), 'Pause', self)
pause_action.triggered.connect(self.pausePlot)
toolbar.addAction(pause_action)
clear_action = QtWidgets.QAction(QtGui.QIcon('icons/clear.png'), 'Clear', self)
clear_action.triggered.connect(self.clearPlot)
toolbar.addAction(clear_action)
exit_action = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
exit_action.triggered.connect(self.close)
toolbar.addAction(exit_action)
# 创建 matplotlib 绘图区域
self.canvas = FigureCanvas(self.figure)
self.canvas.setParent(self)
# 设置GUI窗口中 matplotlib 绘图区域的样式
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.canvas)
self.setLayout(layout)
# 显示 GUI
self.show()
def clearPlot(self):
print("Clear plot")
defpausePlot(self):
print("Pause plot")
def closeEvent(self, event):
# 单击“Close”按钮时关闭窗口
self.pausePlot()
def plotData(self):
# 绘制数据
pass
if __name__ == '__main__':
# 创建GUI应用程序并显示
app = QtWidgets.QApplication(sys.argv)
window = RealTimePlot()
sys.exit(app.exec_())
在上面的代码中,我们首先创建了一个名为 RealTimePlot 的类,并从 QMainWindow 类继承,这个类实现了 GUI 窗口、菜单栏、工具栏和 matplotlib 绘图区域。我们还为该窗口指定了三个工具栏按钮。逐个细分以下这个类中的方法:
- __init__(): 这是 Python 类的构造函数。在这里,我们用 initUi() 方法来初始化 GUI 窗口,并创建一个 matplotlib 绘图区域。
-
initUi(): 这个方法创建了 GUI 窗口、菜单栏、工具栏和 matplotlib 绘图区域。我们还为工具栏按钮指定了图标和名称。
-
clearPlot(): 这个方法将在单击“Clear”按钮时调用,以清除当前绘图。
-
pausePlot(): 这个方法将在单击“Pause”按钮时调用,以暂停实时绘图。
-
closeEvent(): 这是一个 PyQt5 回调函数,当单击窗口上的“Close”按钮时,它将调用 pausePlot() 方法,并关闭 GUI 窗口。
-
plotData(): 这个方法将实时绘图,我们将在下面展示它的实现方式。
接下来我们将实现 plotData() 方法。我们将使用 matplotlib 的动画引擎来实现实时绘图。Matplotlib 允许我们使用 FuncAnimation 类来轻松地实现动画。FuncAnimation 以一个函数作为参数,并返回一个实例化的动画类。在该函数内,我们将获取我们要绘制的数据,绘制数据,然后在图形上绘制。我们将设置一个计时器,每隔一段时间就调用这个函数,以更新数据和图形。
以下是 plotData() 方法的实现方式:
def plotData(self):
# 绘制数据
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
# 清除当前绘图区域并重新绘制数据
self.figure.clear()
ax = self.figure.add_subplot(111)
# 设置图形属性
ax.set_title('Real-time plot')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
# 绘制数据和图形
ax.plot(x, y)
# 更新 matplotlib 绘图区域
self.canvas.draw()
在这个函数中,我们首先生成一组定义域数据,然后计算出它们的 sin 函数值。接下来,我们清除当前绘图区域并重新绘制数据。然后,我们添加一个子图,并设置其标题和轴标签。最后,我们绘制我们的数据(在这种情况下,是一个 sin 函数)并更新 matplotlib 绘图区域。
真正的实时数据绘图应该是基于实时数据而不是固定数据。我们将通过一个计时器每隔几秒更新数据,并在重绘之前删除旧数据。在此之后,我们将建立一个新的方法,以计时器为间隔调用上面的 plotData() 函数,以更新数据和图形。在 RealTimePlot 类中添加新的功能代码如下:
class RealTimePlot(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(RealTimePlot, self).__init__(parent)
# 定时器对象
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.plotData)
self.initUi()
def plotData(self):
# 实时更新数据并重新绘制
pass
def startPlot(self):
# 开始实时绘图
self.timer.start()
def pausePlot(self):
# 暂停实时绘图
self.timer.stop()
def closeEvent(self, event):
# 单击“Close”按钮时关闭窗口
self.pausePlot()
def plot(self):
# 绘制数据
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
# 清除当前绘图区域并重新绘制数据
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.set_title('Real-time plot')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.plot(x, y)
# 更新 matplotlib 绘图区域
self.canvas.draw()
def plotData(self):
# 实时更新数据并重新绘制
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
# 删除旧的数据,这个方法用于优化绘制速度(类似于双缓冲)
self.figure.clf()
# 实时绘图
self.plot()
def clearPlot(self):
# 清除当前绘图
self.figure.clear()
self.canvas.draw()
def initUi(self):
# 创建一个matplotlib图形
self.figure = Figure()
# 为GUI设置一个标题
self.setWindowTitle('Real-time Plot')
# 为GUI设置一个菜单栏
menubar = self.menuBar()
file_menu = menubar.addMenu('File')
exit_action = file_menu.addAction('Exit')
exit_action.triggered.connect(self.close)
# 为GUI设置一个工具栏
toolbar = self.addToolBar('Tool')
start_action = QtWidgets.QAction(QtGui.QIcon('icons/start.png'), 'Start', self)
start_action.triggered.connect(self.startPlot)
toolbar.addAction(start_action)
pause_action = QtWidgets.QAction(QtGui.QIcon('icons/pause.png'), 'Pause', self)
pause_action.triggered.connect(self.pausePlot)
toolbar.addAction(pause_action)
clear_action = QtWidgets.QAction(QtGui.QIcon('icons/clear.png'), 'Clear', self)
clear_action.triggered.connect(self.clearPlot)
toolbar.addAction(clear_action)
exit_action = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
exit_action.triggered.connect(self.close)
toolbar.addAction(exit_action)
# 创建 matplotlib 绘图区域
self.canvas = FigureCanvas(self.figure)
self.canvas.setParent(self)
# 设置GUI窗口中 matplotlib 绘图区域的样式
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.canvas)
self.setLayout(layout)
# 显示 GUI
self.show()
if __name__ == '__main__':
# 创建GUI应用程序并显示
app = QtWidgets.QApplication(sys.argv)
window = RealTimePlot()
sys.exit(app.exec_())
上述代码添加了三个新方法:
- startPlot(): 这个方法将在单击“Start”按钮时调用,并将启动定时器以开始实时绘图。
-
plot(): 这个方法仍然用于绘制数据,但现在它将被调用以实时更新数据和绘图。
-
plotData(): 这个方法将在定时器每隔几秒调用一次,并调用 plot() 方法以实时更新数据和图形。
如您所见,定时器对象是在 __init__() 方法中创建的,并设置间隔为 1000 毫秒(即每秒钟更新一次)。startPlot() 方法将在单击“Start”按钮时调用,以启动这个定时器对象并开始实时绘图。pausePlot() 方法将在单击“Pause”按钮时调用,以暂停实时绘图。closeEvent() 方法将在单击窗口上的“Close”按钮时调用,以停止定时器并关闭 GUI 窗口。
结论
在本文中,我们讨论了如何使用 PyQT5 和 Matplotlib 模块创建实时数据绘图 GUI。我们创建了一个能够实时绘制 sin 函数图形的 GUI,以演示实时数据绘图的过程。我们学习了如何创建 GUI 窗口、菜单栏、工具栏和 matplotlib 绘图区域,以及如何使用 matplotlib 的动画引擎实现实时绘图。最后,我们展示了如何使用 PyQt5 中的定时器对象来实时更新数据,并在 matplotlib 绘图区域中绘制数据。
实时数据绘图在科学研究和工程应用中非常常见,因为它能够帮助我们了解数据如何随时间变化。希望本文对您理解实时数据绘图有所帮助,并且能够启发您创建替代方案以适应您的需求。