Embed a matplotlib graph in a specific widget designed with Designer

I designed a GUI with the designer. In a specific area, I placed a widget Qwidget and I promote it into “MplWidget”.

Now, instead of having a plot in the full windows, I would like to plot it in the dedicated area.

In my main, I have created, before the MainWindow Class, a MplWidget Class

class MplWidget(QWidget):  
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.canvas = FigureCanvas(Figure())

        vertical_layout = QVBoxLayout()
        vertical_layout.addWidget(self.canvas)
        vertical_layout.addWidget(NavigationToolbar(self.canvas, self))

        self.setLayout(vertical_layout)

in my main window class :

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.statusbar.showMessage("Gestionnaire de joueurs - V23.03.07")
        
        self.pushButton.clicked.connect(self.plot_sin_cos)
        self.showMaximized()

 def plot_sin_cos(self):
     """
     # This example display a simple 2D plot
     """
     # -------- Example data generation
     f = random.randint(1, 100)
     length_of_signal = 100
     t = np.linspace(0, 1, length_of_signal)

     cosinus_signal = np.cos(2*np.pi*f*t)
     sinus_signal = np.sin(2*np.pi*f*t)

     # -------- Create and display the plot
     try:
         self.MplWidget.canvas.figure.clf()  # --> Clean the canvas before use
     except:
         print('no canvas to clear')
     # 1) creation of a Canvas that allows a figure
     self.MplWidget.canvas.axes = self.MplWidget.canvas.figure.add_subplot(111)
     # 2) create the cosinus plot
     self.MplWidget.canvas.axes.plot(t, cosinus_signal)
     # 3) create the sinus plot
     self.MplWidget.canvas.axes.plot(t, sinus_signal)
     # 4) pimp legends and titles
     self.MplWidget.canvas.axes.legend(('cosinus', 'sinus'), loc='upper right')
     self.MplWidget.canvas.axes.set_title('Cosinus - Sinus Signals')
     # 5) Display the result
     self.MplWidget.canvas.draw()   

it is not working because the in the GUI.py generated by pyside6-uic, it tries to load a file named mplwidget.

How to fix my problem ?

Hi @lelorrain welcome to the forum!

To load a promoted widget, it needs to be available in a separate file. In your case, that would mean moving the MplWidget definition into a file name mplwidget.py (the error occurs because it’s looking for that file).

The reason for this, is that when you promote a widget you provide a “library” name and a “class” name, e.g. mplwidget.MplWidget. The part in front of the dot is the name of the file where MplWidget can be found.

You can define multiple promotable widgets within the same file, they just have to be in a separate file from where you’re trying to use them.

Hope that makes sense.

Ok, thank you, it is a first issue ^^ but I still can’t display a graph.

To simplify the discution, here are testing codes:

file 1 : main_window_test.py is the GUI generated with the designer. The widget_2 area as been promoted to MplCanvas:

# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'main_window_test.ui'
##
## Created by: Qt User Interface Compiler version 6.2.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
    QFont, QFontDatabase, QGradient, QIcon,
    QImage, QKeySequence, QLinearGradient, QPainter,
    QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QGridLayout, QMainWindow, QMenuBar,
    QPushButton, QSizePolicy, QStatusBar, QWidget)

from mplcanvas import MplCanvas

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(640, 480)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.gridLayout = QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName(u"gridLayout")
        self.widget = QWidget(self.centralwidget)
        self.widget.setObjectName(u"widget")

        self.gridLayout.addWidget(self.widget, 0, 0, 1, 1)

        self.widget_2 = MplCanvas(self.centralwidget)
        self.widget_2.setObjectName(u"widget_2")

        self.gridLayout.addWidget(self.widget_2, 0, 1, 1, 1)

        self.pushButton = QPushButton(self.centralwidget)
        self.pushButton.setObjectName(u"pushButton")

        self.gridLayout.addWidget(self.pushButton, 1, 0, 1, 1)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setObjectName(u"menubar")
        self.menubar.setGeometry(QRect(0, 0, 640, 21))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
        self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None))
    # retranslateUi

file 2 is the mplcanvas.py file

import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure

class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=3, dpi=100):
        self.fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.fig.add_subplot(111)
        
        super(MplCanvas, self).__init__(self.fig)

file 3 main_test.py is the main to execute.

# -*- coding: utf-8 -*-
"""
Created on Thu Mar  9 16:16:27 2023

@author: lelorrain
"""

#---------------------------------------------------------------------#
#-----------------------------Imports---------------------------------#
#---------------------------------------------------------------------#

import sys, os, time

from PySide6 import QtWidgets, QtGui, QtCore, QtCharts
from PySide6.QtCore import Qt

import random
import numpy as np

from main_window_test import *
from mplcanvas import MplCanvas

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        
        self.widget_2 = MplCanvas()       
        self.pushButton.clicked.connect(self.plot_sin_cos)

        self.showMaximized()
        
    def plot_sin_cos(self):
       """
       # This example display a simple 2D plot
       """
       # -------- Example data generation
       f = random.randint(1, 100)
       length_of_signal = 100
       t = np.linspace(0, 1, length_of_signal)

       cosinus_signal = np.cos(2*np.pi*f*t)
       sinus_signal = np.sin(2*np.pi*f*t)

       # -------- Create and display the plot
       try:
           self.MplCanvas.canvas.figure.clf()  # --> Clean the canvas before use
       except:
           print('no canvas to clear')
       # 1) creation of a Canvas that allows a figure
       self.MplCanvas.canvas.axes = self.MplCanvas.canvas.figure.add_subplot(111)
       # 2) create the cosinus plot
       self.MplCanvas.canvas.axes.plot(t, cosinus_signal)
       # 3) create the sinus plot
       self.MplCanvas.canvas.axes.plot(t, sinus_signal)
       # 4) pimp legends and titles
       self.MplCanvas.canvas.axes.legend(('cosinus', 'sinus'), loc='upper right')
       self.MplCanvas.canvas.axes.set_title('Cosinus - Sinus Signals')
       # 5) Display the result
       self.MplCanvas.canvas.draw()   

if __name__ == "__main__":
    import sys
    a = QtWidgets.QApplication(sys.argv)
    b = MainWindow()
    b.show()
    a.exec_()   

but if I try to execute this code, when I push the button I have an error : AttributeError: ‘MainWindow’ object has no attribute ‘MplCanvas’

When you create the canvas object, you store it in the attribute widget_2, e.g.

self.widget_2 = MplCanvas(self.centralwidget)

But then later try and access it through the attribute self.MplCanvas.

self.MplCanvas.canvas.figure.clf()

To use the created mpl canvas object, you should refer to the attribute it is stored in, e.g.

self.widget_2.canvas.figure.clf()

Also update all other references to self.MplCanvas to self.widget_2. You might also want to rename the widget in designer so it has a more intuitive name than widget_2 (you can edit the ObjectName in Designer).

Thank you for your help,

I completly agree with you about names… I renamed in using you examples

self.widget_2.canvas.figure.clf()

because I had this error :

AttributeError: 'MplCanvas' object has no attribute 'canvas'

I also tried to remove the ‘.canvas’ believing it is directly herited. But it is not. So… when I click on the button… Nothing appends :smiley:

I’m really sorry

No need to apologise. I think the problem is that the figure object is actually self.fig on the MplCanvas class. So the line should be

self.widget_2.fig.clf()

and in the main I have to remove the line

        self.widget_2 = MplCanvas()       

This time it works!

Thank you for your help

1 Like

I’m still using PyQt5 for now. This is the MplWidget.py I’ve always used.

from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

class MplCanvas(FigureCanvas):
    '''
    Class to represent the FigureCanvas widget
    '''
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

class MplWidget(QWidget):
    '''
    Widget promoted and defined in Qt Designer
    '''
    def __init__(self, parent = None):
        QWidget.__init__(self, parent)
        self.canvas = MplCanvas()
        self.vbl = QVBoxLayout()
        self.vbl.addWidget(self.canvas)
        self.setLayout(self.vbl)
1 Like