繁体   English   中英

在 pyqt5 gui 中显示 matplotlib 动画 plot 并同时保存在 mp4 中的问题

[英]issues in displaying matplotlib animated plot in pyqt5 gui and save in mp4 at the same time

目标:在mp4中同时显示和保存plot

我可以保存视频,但是当它完成保存时,它开始覆盖导致崩溃的画布,并且不会在 gui 中显示 plot。 我想我必须使用某种线程来运行 animation 但我被困在如何做到这一点。 我也可以想象我的代码中有很多缺陷。

主文件

import matplotlib.animation as animation
from matplotlib import style
import csv
import os.path
from os import path

import matplotlib.animation as animation

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import datetime
import serial.tools.list_ports
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import random
from numpy import diff
import numpy as np
LARGE_FONT = ("Verdana", 12)
import pandas as pd
style.use('fivethirtyeight')


import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem, QPushButton, QLabel, QDialog, QVBoxLayout, QApplication, QLineEdit)
from PyQt5.QtWidgets import (QPushButton, QDialog, QTreeWidget,
                             QTreeWidgetItem, QVBoxLayout,
                             QHBoxLayout, QFrame, QLabel, QComboBox,
                             QApplication, QTreeWidgetItemIterator, QMessageBox, QProxyStyle)
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys

import matplotlib
from matplotlib.figure import Figure
matplotlib.use('QT5Agg')
from matplotlib.backends.backend_qt5agg import  FigureCanvas
from matplotlib.backends.backend_qt5agg import  NavigationToolbar2QT as NavigationToolbar

plt.rcParams['animation.ffmpeg_path'] =  'C:/ffmpeg/bin/ffmpeg.exe'
from anim import Ui_MainWindow
plt.ion()

writer = animation.FFMpegWriter()


plt.show()
class Test(QtWidgets.QMainWindow):
    def __init__(self, parent = None):
        super().__init__(parent)
        QtWidgets.QMainWindow.__init__(self, parent)
        self.i = 0
        self.ui = Ui_MainWindow()
        self.ui.setupUi(parent)
        
        vlay1 = QVBoxLayout()
        self.plotWidget1 = FigureCanvas(Figure())
        toolbar1 = NavigationToolbar(self.plotWidget1, self)
        
        openButton = QtWidgets.QPushButton("Open a file")
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(openButton.sizePolicy().hasHeightForWidth())
        openButton.setSizePolicy(sizePolicy)
        dateTimeEdit = QtWidgets.QDateTimeEdit()
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(dateTimeEdit.sizePolicy().hasHeightForWidth())
        dateTimeEdit.setSizePolicy(sizePolicy)
        
        vlay1.addWidget(openButton)
        vlay1.addWidget(dateTimeEdit)
        vlay1.addWidget(toolbar1)
        vlay1.addWidget(self.plotWidget1)
        self.ui.gridLayout.addLayout(vlay1, 0, 0, 1, 1)
        openButton.clicked.connect(self.openFile)

        self.ax1 = self.plotWidget1.figure.subplots()
        self.line, = self.ax1.plot([], [], marker='o')
        self.ax1.set_xlabel("Real")
        self.ax1.set_ylabel("Imaginary")
        
        self.ax1.relim()
        self.ax1.autoscale_view(True, True, True)
        self.ax1.set_title(str(self.i))
        self.plotWidget1.figure.tight_layout()
        self.openFile()
        anim = animation.FuncAnimation(self.plotWidget1.figure, self.animate, init_func=self.init,
                               frames=self.len-1, interval=1, blit=True)
        anim.save('plot.mp4', writer=writer, dpi=300, )
        

    def readFile(self):
        df = pd.read_excel(self.fileName)
        self.Real1 = df["Real 1"]
        self.Real2 = df["Real 2"]
        self.Real3 = df["Real 3"]
        self.Real4 = df["Real 4"]
        self.Real5 = df["Real 5"]
        self.Img1 = df["Imaginary 1 "]
        self.Img2 = df["Imaginary 2"]
        self.Img3 = df["Imaginary 3"]
        self.Img4 = df["Imaginary 4"]
        self.Img5 = df["Imaginary 5"]
        
        self.len = len(self.Img1)
        

    def getDataX(self):
        self.i += 1
        if self.i == self.len - 1:
            print("complete")
        return [self.Real1[self.i], self.Real2[self.i], self.Real3[self.i], self.Real4[self.i], self.Real5[self.i]]

    def getDataY(self):
       
        return [abs(self.Img1[self.i]), abs(self.Img2[self.i]), abs(self.Img3[self.i]), abs(self.Img4[self.i]), abs(self.Img5[self.i])]
    
        
    def openFile(self):
        dirAndName = QtWidgets.QFileDialog.getOpenFileName(self,'Open a File', "*.xlsx")
        if dirAndName[0] != []:
            self.fileName = dirAndName[0]
            self.readFile()
        print(dirAndName)
        
    def init(self):
        self.line.set_data([], [])
        return self.line,

    def animate(self, i):
        x = self.getDataX()
        y = self.getDataY()
        
        x = np.asarray(x)
        y = np.asarray(y)
        
        self.ax1.set_title(str(self.i))
        self.line.set_data(x, y)
        self.ax1.relim()
        self.ax1.autoscale_view(True, True, True)
        return self.line,

def Home():
    f = QtWidgets.QMainWindow()
    
    #f.resize(1699, 980)
    c = Test(f)
    
    f.show()
    
    r = qApp.exec_()

if __name__ == "__main__":
    qApp = QtWidgets.QApplication(sys.argv)
    qApp.setStyle(QStyleFactory.create('Fusion'))
    proxy = QProxyStyle(qApp.style())
    qApp.setStyle(proxy)
    Home()

动画.py

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
        self.pushButton.setSizePolicy(sizePolicy)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.dateTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.dateTimeEdit.sizePolicy().hasHeightForWidth())
        self.dateTimeEdit.setSizePolicy(sizePolicy)
        self.dateTimeEdit.setObjectName("dateTimeEdit")
        self.verticalLayout.addWidget(self.dateTimeEdit)
        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        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", "Open File"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

*** 我出于其他原因使用 Anim.py,即使我的 main.py 再次创建 qpushbutton 和 qtimeedit ***

免责声明:我不会使用 OP 的代码,因为它包含不必要的元素,只会分散注意力以及无用的导入,因此我将展示一个仅关注所需功能的示例。

如果要保存并显示 GUI,则不应使用 save() 方法,因为这是阻塞的,而应直接使用 writer (FFMpegWriter),使用 setup() 方法进行配置,然后使用 grab_frame() 方法记录帧。 需要注意的是,当记录 animation 的速度时,由于记录一帧会消耗时间,从而使 eventloop 忙碌,因此会降低速度。

import sys

import numpy as np

from PyQt5 import QtCore, QtWidgets

from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from matplotlib import animation


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.animation_button = QtWidgets.QPushButton("Start", checkable=True)

        self.canvas = FigureCanvas(Figure(figsize=(5, 3)))

        self.ax = self.canvas.figure.subplots()

        t = np.linspace(0, 10, 101)
        x, y = self.generate_data(0)
        (self._line,) = self.ax.plot(x, y)

        self.writer = animation.FFMpegWriter()
        self.writer.setup(self.canvas.figure, outfile="plot.mp4", dpi=300)

        self.anim = animation.FuncAnimation(
            self.canvas.figure,
            self.callback_animation,
            frames=200,
            interval=20,
            blit=True,
        )

        self.animation_button.toggled.connect(self.handle_toggled)

        central_widget = QtWidgets.QWidget()

        self.setCentralWidget(central_widget)
        layout = QtWidgets.QVBoxLayout(central_widget)
        layout.addWidget(self.canvas)
        layout.addWidget(self.animation_button)

    def callback_animation(self, i):
        x, y = self.generate_data(i)
        self._line.set_data(x, y)
        if self.animation_button.isChecked():
            self.writer.grab_frame()
        return (self._line,)

    def generate_data(self, i):
        x = np.linspace(0, 2, 1000)
        y = np.sin(2 * np.pi * (x - 0.01 * i))

        return x, y

    def handle_toggled(self):
        self.animation_button.setText(
            "Stop" if self.animation_button.isChecked() else "Start"
        )

    def closeEvent(self, event):
        super().closeEvent(event)
        self.writer.finish()


if __name__ == "__main__":
    qapp = QtWidgets.QApplication.instance()
    if not qapp:
        qapp = QtWidgets.QApplication(sys.argv)

    app = ApplicationWindow()
    app.show()
    app.activateWindow()
    app.raise_()
    qapp.exec_()

暂无
暂无

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

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