PyQT5+matplotlib FuncAnimation动态绘制曲线图

本博客介绍了一个使用Python结合Qt5和Matplotlib实现的实时数据绘图应用程序。该程序通过FuncAnimation更新画布上的图表,同时利用Qt5 GUI进行用户交互,包括输入数据点并在文本浏览器中显示。

在这里插入图片描述

#! /usr/bin/env python3
# coding: utf-8

import sys
import os
import random

import matplotlib
# Make sure that we are using QT5
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout,QHBoxLayout, QSizePolicy, QWidget, QTextBrowser, QLineEdit
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
 
progname = os.path.basename(sys.argv[0])
progversion = "0.1"
 
class MyMplCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)

        self.ax = fig.add_subplot(1,1,1)

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)
        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
 
class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowTitle("application main window")
        self.main_widget = QtWidgets.QWidget(self)
        self.xs = []
        self.ys = []
        vbox = QtWidgets.QVBoxLayout(self.main_widget)
 
        self.canvas =  MyMplCanvas( self.main_widget,width=6, height=6, dpi=100) ###attention###
        vbox.addWidget(self.canvas)
 
        hbox = QtWidgets.QHBoxLayout(self.main_widget)
        self.textBrowser = QTextBrowser(self)
        self.lineEdit = QLineEdit(self)
 
        vbox.addWidget(self.textBrowser)
        vbox.addWidget(self.lineEdit)
 
        self.setLayout(vbox)
 
        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)
        self.lineEdit.returnPressed.connect(self.update_text)
 
        self.ani = FuncAnimation(self.canvas.figure, self.update_line, interval=10)
 
    def update_line(self, i):
        self.canvas.ax.clear()
        self.canvas.ax.plot(self.xs, self.ys)
 
    def update_text(self):
        self.text = self.lineEdit.text()
        self.textBrowser.append(self.text)
        x, y = self.text.split(',')
        self.xs.append(float(x))
        self.ys.append(float(y))
 
if __name__ == "__main__":
    App = QApplication(sys.argv)
    aw = ApplicationWindow()
    aw.show()
    App.exit()
    sys.exit(App.exec_())
<think>我们正在使用PyQt5Matplotlib创建动态图表。动态图表意味着图表会随着数据的变化而实时更新。在PyQt5中嵌入Matplotlib图表,并使其动态更新,通常涉及以下步骤: 1. 创建Matplotlib图形和坐标轴。 2. 将Matplotlib图形嵌入到PyQt5的QWidget中(使用FigureCanvasQTAgg)。 3. 初始化图表数据,并绘制初始图表。 4. 使用定时器(QTimer)定期更新数据并重绘图表。 下面我们将逐步说明如何实现: 步骤1:导入必要的库 ```python import sys import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget from PyQt5.QtCore import QTimer ``` 步骤2:创建一个继承自QWidget的窗口,用于容纳Matplotlib画布 ```python class DynamicPlotWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) # 创建图形和坐标轴 self.figure, self.ax = plt.subplots() # 创建画布,并将图形设置到画布上 self.canvas = FigureCanvas(self.figure) # 设置布局 layout = QVBoxLayout() layout.addWidget(self.canvas) self.setLayout(layout) # 初始化数据 self.x = np.linspace(0, 10, 100) self.y = np.sin(self.x) # 绘制初始曲线 self.line, = self.ax.plot(self.x, self.y) self.ax.set_xlim(0, 10) self.ax.set_ylim(-1.5, 1.5) # 创建定时器 self.timer = QTimer(self) self.timer.timeout.connect(self.update_plot) self.timer.start(100) # 每100毫秒更新一次 def update_plot(self): # 更新数据:这里我们让正弦波向右移动 self.y = np.sin(self.x - 0.1 * self.update_plot.counter) self.line.set_ydata(self.y) self.canvas.draw_idle() # 重新绘制画布 # 计数器,用于记录更新次数 self.update_plot.counter += 1 # 给函数添加一个属性,用于计数 update_plot.counter = 0 ``` 步骤3:创建主窗口并显示 ```python class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("动态图表示例") self.setGeometry(100, 100, 800, 600) # 创建动态图表窗口 self.plot_widget = DynamicPlotWindow() self.setCentralWidget(self.plot_widget) if __name__ == '__main__': app = QApplication(sys.argv) mainWin = MainWindow() mainWin.show() sys.exit(app.exec_()) ``` 说明: - 我们创建了一个`DynamicPlotWindow`类,它包含一个Matplotlib画布。 - 在`__init__`中,我们设置了初始的正弦波形,并启动了一个定时器。 - 定时器每隔100毫秒触发一次`update_plot`函数,该函数更新数据并重新绘制图表。 - 注意:我们使用`draw_idle`来更新画布,这样它会在应用程序空闲时重绘,避免过度刷新。 优化点: 1. 如果数据量很大,可以考虑只更新数据而不重新绘制整个图形,这样效率更高(如示例中只更新了`ydata`)。 2. 如果需要在后台进行复杂计算,可以考虑使用多线程,避免阻塞GUI线程。 另外,关于设置背景透明的问题,可以参考引用[3]中的方法,设置网格线的透明度。同样,我们也可以设置整个图表区域的背景透明: ```python # 设置图形背景透明 self.figure.patch.set_alpha(0.0) # 设置坐标轴背景透明 self.ax.patch.set_alpha(0.0) ``` 这样,图表的背景就会透明,可以融入PyQt5窗口的背景中。 注意:动态更新图表时,要避免频繁地创建和销毁对象,而是重用线条对象(如示例中的`self.line`)。 以上就是一个简单的动态图表实现。根据实际需求,可以更改更新函数中的数据生成部分,例如从传感器读取数据或计算新的数据点。 引用说明: 我们在嵌入MatplotlibPyQt5时参考了官网示例和常见做法[^1][^2],同时背景透明设置参考了引用[^3]中的方法。 相关问题: 1. 如何优化PyQt5Matplotlib动态图表的性能? 2. 如何在动态图表中添加多个曲线? 3. 如何实现动态散点图? 4. 如何从外部设备(如串口)获取数据并实时更新图表?</think>### 在PyQt5中使用Matplotlib创建动态图表的方法 在PyQt5中实现Matplotlib动态图表的核心是通过**定时更新数据**和**重绘画布**来实现动态效果。以下是完整实现步骤: --- #### 1. **基本框架搭建** ```python import sys import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget from PyQt5.QtCore import QTimer class DynamicPlotWindow(QMainWindow): def __init__(self): super().__init__() # 创建Matplotlib图形 self.figure, self.ax = plt.subplots() self.canvas = FigureCanvas(self.figure) # 设置布局 central_widget = QWidget() layout = QVBoxLayout(central_widget) layout.addWidget(self.canvas) self.setCentralWidget(central_widget) # 初始化数据 self.x = np.linspace(0, 10, 100) self.y = np.sin(self.x) # 绘制初始曲线 self.line, = self.ax.plot(self.x, self.y) self.ax.set_xlim(0, 10) self.ax.set_ylim(-1.5, 1.5) # 创建定时器 self.timer = QTimer(self) self.timer.timeout.connect(self.update_plot) self.timer.start(100) # 每100毫秒更新一次 def update_plot(self): """动态更新图表数据""" self.y = np.sin(self.x - 0.1 * self.update_plot.counter) # 移动波形 self.line.set_ydata(self.y) # 更新Y轴数据 self.canvas.draw() # 重绘画布 self.update_plot.counter += 1 # 给函数添加计数器属性 update_plot.counter = 0 if __name__ == "__main__": app = QApplication(sys.argv) window = DynamicPlotWindow() window.show() sys.exit(app.exec_()) ``` --- #### 2. **关键技术点说明** 1. **嵌入Matplotlib**: - 使用`FigureCanvasQTAgg`将Matplotlib图形嵌入PyQt5窗口 - 通过`FigureCanvas`的`draw()`方法实现画布刷新[^1][^2] 2. **动态更新机制**: ```python # 定时器设置 self.timer = QTimer(self) self.timer.timeout.connect(self.update_plot) # 连接更新函数 self.timer.start(100) # 更新间隔(毫秒) ``` 3. **高效数据更新**: ```python # 只更新数据点而非重新绘图 self.line.set_ydata(new_data) # 比重新plot快10倍 self.canvas.draw_idle() # 优化版重绘方法 ``` 4. **背景透明设置**(可选): ```python # 设置图形背景透明 self.figure.patch.set_alpha(0.0) # 设置坐标轴背景透明 self.ax.patch.set_alpha(0.0) # 设置网格线透明度 self.ax.grid(alpha=0.5) # [^3] ``` --- #### 3. **动态图表类型扩展** 1. **实时数据流图表**: ```python def update_plot(self): new_point = np.random.rand() # 模拟实时数据 self.y = np.roll(self.y, -1) # 左移数据 self.y[-1] = new_point # 添加新数据点 self.line.set_ydata(self.y) self.canvas.draw_idle() ``` 2. **动态散点图**: ```python # 初始化 self.scatter = self.ax.scatter(x, y) # 更新 def update_scatter(): new_positions = np.random.rand(100, 2) self.scatter.set_offsets(new_positions) self.canvas.draw_idle() ``` 3. **3D动态图**: ```python from mpl_toolkits.mplot3d import Axes3D # 初始化3D坐标轴 self.ax = self.figure.add_subplot(111, projection='3d') # 更新Z轴数据 def update_3d(): Z = np.sin(X + counter) * np.cos(Y + counter) self.surface.set_array(Z.ravel()) ``` --- #### 4. **性能优化技巧** 1. **使用`blit`技术**: ```python # 初始化时缓存背景 self.ax_background = self.canvas.copy_from_bbox(self.ax.bbox) # 更新时只重绘变化部分 def update_with_blit(): self.canvas.restore_region(self.ax_background) self.line.set_ydata(new_data) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox) ``` 2. **控制更新频率**: ```python # 根据数据速率调整 if data_source.fps > 30: self.timer.setInterval(30) # 限制最高30FPS ``` 3. **避免内存泄漏**: ```python # 在窗口关闭时停止定时器 def closeEvent(self, event): self.timer.stop() event.accept() ``` --- #### 5. **实际应用场景** 1. **传感器数据监控** ```python # 从串口读取实时数据 def read_serial_data(): return serial_port.readline().decode().strip() ``` 2. **金融实时行情** ```python # 通过API获取股票数据 def get_stock_data(symbol): return yfinance.Ticker(symbol).history(period='1d', interval='1m') ``` 3. **科学计算可视化** ```python # 求解微分方程并实时显示 def solve_ode(): return scipy.integrate.solve_ivp(...) ``` > 提示:对于高频数据更新(>100Hz),建议使用PyQtGraph库替代Matplotlib以获得更好性能[^1]。 --- ### 常见问题解决 - **图表闪烁问题**:启用`blit`技术或使用`draw_idle()`代替`draw()` - **CPU占用过高**:降低定时器频率,避免在更新函数中进行复杂计算 - **坐标轴动态缩放**: ```python self.ax.relim() # 重设坐标范围 self.ax.autoscale_view() # 自动缩放 ``` 通过结合PyQt5的事件循环和Matplotlib绘图能力,可以实现各种复杂的动态数据可视化应用[^1][^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值