在 pyqt5 界面中动态更新 matplotlib 画布
Posted
技术标签:
【中文标题】在 pyqt5 界面中动态更新 matplotlib 画布【英文标题】:Dynamically update matplotlib canvas in a pyqt5 interface 【发布时间】:2019-08-19 18:06:29 【问题描述】:我有一个非常简单的程序,必须保留结构(这是练习的条件)。
我们从一个由按钮和 Canvas 组成的界面开始,如上图所示。
单击按钮后,将启动一个后台任务,该任务调用一个名为animation
的函数。在这里,我们开始一个每次waiting_time
生成随机数据的过程。
我们想在每次有新的 x 和 y 变量时更新绘图。
代码如下:
from PyQt5 import QtCore, QtWidgets
from mplwidget import MplWidget
import threading
import time
import numpy as np
import sys
class RandomDataGeneration():
"""
Mandatory Class. This Class must exist.
"""
def __init__(self):
pass
def data_generation(self):
while True:
waiting_time = np.random.randint(1,4) # waiting time is given by a random number.
print(waiting_time)
time.sleep(waiting_time)
self.x = np.random.rand(10)
self.y = np.random.rand(10)
print(self.x)
print(self.y)
#self.update_plot()
def update_plot(self):
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.set_title('GRAPH')
self.MplWidget.canvas.axes.plot(x, y, marker='.', linestyle='')
self.MplWidget.canvas.axes.legend(('random'), loc='upper right')
self.MplWidget.canvas.draw()
def animation():
"""
This function initiates the RandomDataGeneration
"""
app = RandomDataGeneration()
app.data_generation()
class Ui_MainWindow():
def __init__(self):
super().__init__()
def start_download(self):
download_info = threading.Thread(target=animation)
download_info.start()
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1280, 1024)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(880, 80, 221, 32))
self.pushButton.setObjectName("pushButton")
self.MplWidget = MplWidget(self.centralwidget)
self.MplWidget.setGeometry(QtCore.QRect(49, 39, 771, 551))
self.MplWidget.setObjectName("MplWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton.clicked.connect(self.start_download)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
要运行代码,必须在同一个文件夹中包含名称为mplwidget.py
的以下代码。
# ------------------------------------------------------
# -------------------- mplwidget.py --------------------
# ------------------------------------------------------
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
【问题讨论】:
允许更改哪些位?例如,您是否允许更改RandomDataGeneration
的实现?
原则上,我们可以更改RandomDataGeneration中除了data_generation之外的所有内容。 update_plot() 已经是我的“贡献”了。
【参考方案1】:
由于您正在创建一个 pyqt5 应用程序,我猜您最好的选择是使用 QThread
作为阻塞部分,并在每次生成新数据时发出一个信号。一种方法是使RandomDataGeneration
成为QThread
的子类并实现run
,例如
class RandomDataGeneration(QtCore.QThread):
"""
Mandatory Class. This Class must exist.
"""
new_data = QtCore.pyqtSignal()
def __init__(self, parent = None):
super().__init__(parent)
def data_generation(self):
while True:
waiting_time = np.random.randint(1,4) # waiting time is given by a random number.
print(waiting_time)
time.sleep(waiting_time)
self.x = np.random.rand(10)
self.y = np.random.rand(10)
print(self.x)
print(self.y)
self.new_data.emit()
def run(self):
self.data_generation()
要使用线程,您可以继承QMainWindow
并在其中创建RandomDataGeneration
的实例。子类化QMainWindow
有一个额外的优势,您可以在其中移动 gui 设置和信号槽连接,例如
class MyMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.download_thread = RandomDataGeneration(self)
self.download_thread.new_data.connect(self.plot_data)
self.ui.pushButton.clicked.connect(self.start_download)
def start_download(self):
if not self.download_thread.isRunning():
self.download_thread.start()
def plot_data(self):
self.ui.MplWidget.update_plot(self.download_thread.x, self.download_thread.y)
主要部分就变成了
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = MyMainWindow()
MainWindow.show()
sys.exit(app.exec_())
【讨论】:
以上是关于在 pyqt5 界面中动态更新 matplotlib 画布的主要内容,如果未能解决你的问题,请参考以下文章
Python GUI界面开发环境配置:Pycharm+PyQt5