I am building a GUI with PyQt6 which has a part for graph visualization based on matplotlib.
The graphs to be visualized are entirely built inside functions that return an already assembled and ready to be plotted matplotlib's figure object - in this case, a matplotlib.figure.Figure
object not natively managed by pyplot
(and I preferrable wouldn't change this). Now, I am struggling to discover how can I (or even if it is possible) to plot a figure object in the same matplotlib canvas that a previous figure object has been plotted, without erasing the previous plot.
Here is a minimum working example of the matplotlib widget I have. The method "update_figure" is used to plot the first figure object to the canvas (and it is already working). Then, the method "add_figure" would be used to plot the subsequent figure objects. In both methods, the variable "new_figure" is a matplotlib figure object.
# ------------------------------------------------------
# -------------------- mplwidget.py --------------------
# ------------------------------------------------------
from PyQt6.QtWidgets import*
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.figure import Figure
from PyQt6.QtGui import QFont, QFontInfo
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Qt5Agg')
class MplWidget(QWidget):
def __init__(self, parent = None):
super(QWidget, self).__init__()
self.canvas = FigureCanvasQTAgg(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.canvas.draw()
#Get the default font style from the system and apply to the canvas
self.resetFontStyle()
self.setLayout(vertical_layout)
def update_figure(self, new_figure):
# new_figure is a matplotlib figure object
# Clear the existing figure content
self.canvas.figure.clf()
self.canvas.axes.cla()
# Copy the contents of the new figure onto the canvas
self.canvas.figure = new_figure
# Redraw the canvas
self.canvas.draw()
def add_figure(self, new_figure):
# new_figure is a matplotlib figure object
#This is what I want to implement
I tried to see if there would be some way to copy the entire information of the figure object and inserted in the current axis of the canvas, but had no luck with that.
In case anyone come across this post, I have devised two alternatives to deal with this problem:
copy
module and the deepcopy
method to copy the new_figure
object into self.canvas.figure
. This appears to take some time to update the figureself.canvas.figure.clf()
and self.canvas.axes.cla()
. This appears (up until now in my tests) to allow the canvas to be refreshed.My original problem is that I did not notice that when I was passing new_figure
to the function update_figure
, and "assigning" it to self.canvas.figure
, what I was really doing was making self.canvas.figure
refer to the same matplotlib.figure.Figure object. Then, when I was trying to update the canvas with a new_figure
, the methods self.canvas.figure.clf()
and self.canvas.axes.cla()
were acting upon the same object as referred by new_figure
, virtually erasing its data.
The reason the first solution above works is because when deepcoping an object, we decouple self.canvas.figure
from new_figure
, and then can perform .clf()
and .clear()
The second solution is, however, my preferred because it does not involve duplicating objects. I don't recall why now, but I assumed that simple calling self.canvas.draw()
would stack up old plots and I would have to clear the figure first. But now I notice this was not the case and the GUI seems to work as intended.