简体   繁体   English

在pyqt5界面中动态更新matplotlib canvas

[英]Dynamically update matplotlib canvas in a pyqt5 interface

I have a very simple program and the structure must be preserved (it is a condition of the exercise). 我有一个非常简单的程序,必须保留结构(这是练习的条件)。

We start with an interface formed by a button and a Canvas, like shown in the figure above. 我们从一个按钮和一个Canvas组成的界面开始,如上图所示。

在此处输入图片说明

Once the button is clicked, a background task is initiated, which calls a function called animation . 单击该按钮后,将启动一个后台任务,该任务将调用一个名为animation的函数。 In here we start a process that generates random data every time waiting_time . 在这里,我们开始一个过程,每次waiting_time生成随机数据。 We want to update the plot everytime there is a new x and y variable. 每当有新的x和y变量时,我们要更新图。

The code is the following: 代码如下:

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_())

To run the code is necessary to have in the same folder the following code with name mplwidget.py . 要运行该代码,必须在同一文件夹中包含名称为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)

Since you are creating a pyqt5 application I guess your best bet is to use a QThread for the blocking part and emit a signal every time new data is generated. 由于您正在创建pyqt5应用程序,所以我猜您最好的选择是对阻塞部分使用QThread并在每次生成新数据时发出信号。 One way would be to make RandomDataGeneration a subclass of QThread and implement run , eg 一种方法是使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()

To use the thread you could subclass QMainWindow and create an instance of RandomDataGeneration in there. 要使用该线程,可以将QMainWindow子类化,并在其中创建RandomDataGeneration的实例。 Subclassing QMainWindow has an additional advantage that you can move the gui setup and the signal-slot connections in there as well, eg 子类化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)

The main part then becomes 然后主要部分变成

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = MyMainWindow()
    MainWindow.show()
    sys.exit(app.exec_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM