Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedding second animated graph to PyQt5 GUI

I want to add a second animated graph to the GUI holding my first animated graph with both graphs animating at the same time, but I'm not sure how.

Here is my code :

import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
from matplotlib import animation


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)

        self.fig = Figure(figsize=(5, 3))
        self.canvas = FigureCanvas(self.fig)
        layout.addWidget(self.canvas)
        self.addToolBar(NavigationToolbar(self.canvas, self))

        self.setup()

    def setup(self):
        self.ax = self.fig.subplots()
        self.ax.set_aspect('equal')
        self.ax.grid(True, linestyle = '-', color = '0.10')
        self.ax.set_xlim([-15, 15])
        self.ax.set_ylim([-15, 15])

        self.scat = self.ax.scatter([], [], c=(0.9, 0.1, 0.5),  zorder=3)
        self.scat.set_alpha(0.8)

        self.anim = animation.FuncAnimation(self.fig, self.update,
                                                 frames = 720, interval = 10)

    def update(self, i):

        self.scat.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))

if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()

And here is some sample code which has two graphs inside the same window (like how I want to) Here they use an _update_canvas function for the animated graph and the other graph (which is just a static graph) they plot it in the application window class. I'm using an update plot function to animate my graph, do I need a second update plot function? How?

Sample code:

import sys
import time
import numpy as np

from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
    from matplotlib.backends.backend_qt5agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
    from matplotlib.backends.backend_qt4agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)

        static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
        layout.addWidget(static_canvas)
        self.addToolBar(NavigationToolbar(static_canvas, self))

        dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
        layout.addWidget(dynamic_canvas)
        self.addToolBar(QtCore.Qt.BottomToolBarArea,
                        NavigationToolbar(dynamic_canvas, self))

        self._static_ax = static_canvas.figure.subplots()
        t = np.linspace(0, 10, 501)
        self._static_ax.plot(t, np.tan(t), ".")

        self._dynamic_ax = dynamic_canvas.figure.subplots()
        self._timer = dynamic_canvas.new_timer(
            100, [(self._update_canvas, (), {})])
        self._timer.start()

    def _update_canvas(self):
        self._dynamic_ax.clear()
        t = np.linspace(0, 10, 101)
        # Shift the sinusoid as a function of time.
        self._dynamic_ax.plot(t, np.sin(t + time.time()))
        self._dynamic_ax.figure.canvas.draw()


if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()
like image 624
Edgar Nash Avatar asked Feb 15 '26 05:02

Edgar Nash


2 Answers

enter image description here

To embed multiple animated graphs, you need to create multiple plot objects(Figure and FigureCanvas) then add each object to the QVBoxLayout. If you wanted to display it horizontally, you can use a QHBoxLayout. Each plot object will have its own subplot, grid, and data. To update each plot's data, you will need each plot to have its individual plot update function where you can pass this to the animation.FuncAnimation handler. So in your case, to have two animated graphs, you will need two update plot functions.

from matplotlib.figure import Figure
from matplotlib import animation
import numpy as np
import sys, matplotlib
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)

class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)

        # Configure figure 1 
        self.fig1 = Figure(figsize=(5, 3))
        self.canvas1 = FigureCanvas(self.fig1)

        # Configure figure 2
        self.fig2 = Figure(figsize=(5, 3))
        self.canvas2 = FigureCanvas(self.fig2)

        layout.addWidget(self.canvas1)
        layout.addWidget(self.canvas2)
        self.addToolBar(NavigationToolbar(self.canvas1, self))
        self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.canvas2, self))

        self.setup()

    def setup(self):
        # Plot 1 (top)
        self.ax1 = self.fig1.subplots()
        self.ax1.set_aspect('equal')
        self.ax1.grid(True, linestyle = '-', color = '0.10')
        self.ax1.set_xlim([-15, 15])
        self.ax1.set_ylim([-15, 15])

        # Plot 2 (bottom)
        self.ax2 = self.fig2.subplots()
        self.ax2.set_aspect('equal')
        self.ax2.grid(True, linestyle = '-', color = '0.10')
        self.ax2.set_xlim([-15, 15])
        self.ax2.set_ylim([-15, 15])

        self.scat1 = self.ax1.scatter([], [], c=(0.9, 0.1, 0.5),  zorder=3)
        self.scat1.set_alpha(0.8)

        self.scat2 = self.ax2.scatter([], [], c=(0.9, 0.1, 0.5),  zorder=3)
        self.scat2.set_alpha(0.8)

        self.anim1 = animation.FuncAnimation(self.fig1, self.update1,frames = 720, interval = 10)
        self.anim2 = animation.FuncAnimation(self.fig2, self.update2,frames = 720, interval = 10)

    # Update data for plot 1
    def update1(self, i):
        self.scat1.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))

    # Update data for plot 2
    def update2(self, i):
        self.scat2.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))

if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()
like image 55
nathancy Avatar answered Feb 17 '26 18:02

nathancy


Is there a specific reason you need two separate canvas and two separate figures? If not, then I agree with ImportanceOfBeingErnest's comment and that you should create only one figure/canvas with 2 subplots, and call a single update function that takes care of updating the content of both axes.

In essence, your question would be a duplicate of this one, except for the fact that you are embedding the animation in a Qt app.

import sys
import time
import numpy as np

from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
    from matplotlib.backends.backend_qt5agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
    from matplotlib.backends.backend_qt4agg import (
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)

        self.fig = Figure(figsize=(5, 6))
        self.canvas = FigureCanvas(self.fig)
        layout.addWidget(self.canvas)
        self.addToolBar(QtCore.Qt.BottomToolBarArea,
                        NavigationToolbar(self.canvas, self))

        self._axs = self.fig.subplots(2, 1)

        self._timer = self.canvas.new_timer(
            100, [(self._update_canvas, (), {})])
        self._timer.start()

    def _update_canvas(self):
        [ax.clear() for ax in self._axs]
        t = np.linspace(0, 10, 501)
        self._axs[0].plot(t, np.tan(t + time.time()), ".")

        t = np.linspace(0, 10, 101)
        self._axs[1].plot(t, np.sin(t + time.time()))
        self.canvas.draw()


if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()
like image 37
Diziet Asahi Avatar answered Feb 17 '26 18:02

Diziet Asahi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!