Reputation: 101
Goal: display and save plot in mp4 at the same time
I am able to save the video but when it finishes saving, it starts to override canvases which leads to crashes and doesnt show the plot in gui. I imagine i have to use some sort of threads to run animation but i am stuck how to do that. I can also imagine a lot flaws in my code.
main.py
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()
Anim.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_())
*** I am using Anim.py for other reasons, even though my main.py is creating qpushbutton and qtimeedit again***
Upvotes: 0
Views: 399
Reputation: 244212
Disclaimer: I am not going to use the OP's code as it has unnecessary elements that only distract as well as useless imports so I am going to show an example focused only on the required functionality.
If you want to save and show the GUI then you should not use the save() method as this is blocking, instead you should directly use the writer (FFMpegWriter), using the setup() method to configure and then the grab_frame() method to record the frames. It should be noted that when recording the speed of the animation decreases since recording a frame consumes time making the eventloop busy.
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_()
Upvotes: 2