简体   繁体   中英

Calling FuncAnimation() when it is inside a function

I am trying to execute FuncAnimation() while calling an specific function. However, I do not obtain any result in my plot.

When FuncAnimation is not inside a function (or inside __init__()) then it works perfectly.

I tried all the already posted solutions related to that issue but they do not work in my code.

My simplified code is:

import sys
import os
from datetime import datetime
import matplotlib
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QSizePolicy, QWidget
import numpy as np
import random
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.animation import FuncAnimation

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.main_widget = QtWidgets.QWidget(self)
        self.canvas =  MyMplCanvas( self.main_widget,width=6, height=6, dpi=100) 

        self.xdata = []
        self.ydata = []

        vbox = QtWidgets.QVBoxLayout(self.main_widget)

        self.start_button = QtWidgets.QPushButton(text="START")
        self.outer_ani = self.start_button.clicked.connect(self.start_func_animation)

        vbox.addWidget(self.canvas)
        vbox.addWidget(self.start_button)
        self.setLayout(vbox)
        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)

    def start_func_animation(self):
        self.line, = self.canvas.ax.plot_date(self.xdata, self.ydata, linestyle='-', linewidth=0.6, markersize=2.5)
        self.ani = FuncAnimation(self.canvas.figure, self.update_line, interval=1000)

        return self.ani

    def update_line(self, i):
        self.xdata.append(datetime.now())
        self.ydata.append(1+np.random.randint(-3,3))

        self.line.set_data(self.xdata, self.ydata)
        self.canvas.figure.gca().relim()
        self.canvas.figure.gca().autoscale_view()
        self.canvas.ax.tick_params(axis='x', rotation=50)

        return self.line,


if __name__ == "__main__":
    App = QApplication(sys.argv)
    aw = ApplicationWindow()
    aw.show()
    App.exit()
    sys.exit(App.exec_())

The solution is to update the painting for it there are 2 options: use self.canvas.draw() or set blit=True in FuncAnimation .

In addition to the above you also have another error is to presume that the connection between a signal and a slot returns the result of the invoked function and that is false, the connection returns a variable that indicates whether the connection was successful or not. Another mistake is that you unnecessarily set the layout in the QMainWindow and that is not possible because the QMainWindow already has a default layout. Finally using quit() doesn't make sense.

from datetime import datetime
import random
import os
import sys

from PyQt5 import QtCore, QtWidgets

import numpy as np

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.animation import FuncAnimation


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.main_widget = QtWidgets.QWidget(self)
        self.canvas = MyMplCanvas(self.main_widget, width=6, height=6, dpi=100)

        self.xdata = []
        self.ydata = []

        vbox = QtWidgets.QVBoxLayout(self.main_widget)

        self.start_button = QtWidgets.QPushButton(text="START")
        # self.outer_ani = self.start_button.clicked.connect(self.start_func_animation)
        self.start_button.clicked.connect(self.start_func_animation)

        vbox.addWidget(self.canvas)
        vbox.addWidget(self.start_button)
        # self.setLayout(vbox)
        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)

    def start_func_animation(self):
        (self.line,) = self.canvas.ax.plot_date(
            self.xdata, self.ydata, linestyle="-", linewidth=0.6, markersize=2.5
        )
        self.canvas.draw()
        self.ani = FuncAnimation(self.canvas.figure, self.update_line, interval=1000)
        # or
        # self.ani = FuncAnimation(self.canvas.figure, self.update_line, interval=1000, blit=True)
        # return self.ani

    def update_line(self, i):
        self.xdata.append(datetime.now())
        self.ydata.append(1 + np.random.randint(-3, 3))

        self.line.set_data(self.xdata, self.ydata)
        self.canvas.figure.gca().relim()
        self.canvas.figure.gca().autoscale_view()
        self.canvas.ax.tick_params(axis="x", rotation=50)

        return (self.line,)


if __name__ == "__main__":
    App = QtWidgets.QApplication(sys.argv)
    aw = ApplicationWindow()
    aw.show()
    # App.exit()
    sys.exit(App.exec_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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