matplotlibqt5pyside2qwidget

Pyplot (matplotlib) plot in QWidget on dialog page in Python


I have an application that has worked for over a year. I added a new section. This section is opens a dialog upon pressing a button. In this dialog, I put a bland QWidget. I want to plot using matplotlib on that widget. Below I will show what I tried to use as a basis. I am not able to get this task done. Many of the example I took did not work at all - i.e. when I run the application, I get messages that methods do not exist. There were cases where I fixed those programming errors and then the plot opened in another window but not where I wanted it to open. Is there a humane way to put a matplotlib plot on a qwidget? If not, what should I do to get a plot there. I'm coming close to declaring this cannot be done. Please point me in the correct direction. I am trying to do this in python3 - not in python2 - the way the examples are written could have been from python2 and perhaps the examples are out dated.

I looked at

1- Plotting matplotlib figure inside QWidget using Qt Designer form and PyQt5. Note, I am using pyside2 and not qt5 directly.

2- https://www.pythonguis.com/tutorials/pyside-plotting-matplotlib/

3- https://forum.pythonguis.com/t/embed-a-matplotlib-graph-in-a-specific-widget-designed-with-designer/1066

Note- My qwidget is not the "central widget" it is a specific widget that I defined in the qt-designer at a specific place. In the end there will be several like this.

I expected that I would figure out a way to plot in the place that I wanted to plot. In the end, this did not work. Either I got errors or the plot was plotted in a new window that was opened up.

This code: I edited the code - sorry for the prints, it's my way to know where I got:

from PySide2.QtWidgets import *
import PySide2.QtWidgets as QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
from matplotlib.figure import Figure
import matplotlib
matplotlib.use('Qt5Agg')


class MplCanvas(Canvas):
    '''
    Class to represent the FigureCanvas widget
    '''
    def __init__(self):
        print('7')
        self.fig = Figure()
        print('8')
        self.ax = self.fig.add_subplot(111)
        print('9')
        super(MplCanvas, self).__init__(self.fig)
        print('10')
        #Canvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
        print('11')
        Canvas.updateGeometry(self)
        print('12')




class MplWidget(QWidget):
    '''
    Widget promoted and defined in Qt Designer
    '''
    def __init__(self, parent=None):
        #QWidget.__init__(self, parent)
        super(MplWidget, self).__init__(parent)
        print('1')
        self.vbl = QVBoxLayout()
        print('2')
        #self.canvas = Canvas(Figure())
        print('3')
        self.canvas = MplCanvas()
        print('3.5')
        self.vbl.addWidget(self.canvas)
        #self.vbl.addWidget(parent)
        print('4')
        self.setLayout(self.vbl)
        print('5')
        self.checkCanvas()
        print('5')

    def checkCanvas(self):
        print(self.canvas)

    def getCanvas(self):
        return self.canvas

When I run just the constuctor: plotWidget = MplWidget(self.ink_limit_master_dialog.pyplot_widget)

I print:

1
2
3
7
8
9
10
11
12
3.5

But Then I get the following errors:

Traceback (most recent call last):
  File "C:/Work/ResourceGenerator/ResCreatorNewGeneration/res_create_NewGen_uic.py", line 1109, in ink_limit_master_callback
    plotWidget = MplWidget(self.ink_limit_master_dialog.pyplot_widget)
  File "C:\Work\ResourceGenerator\ResCreatorNewGeneration\MplWidgetClass.py", line 70, in __init__
    self.vbl.addWidget(self.canvas)
TypeError: 'PySide2.QtWidgets.QBoxLayout.addWidget' called with wrong argument types:
  PySide2.QtWidgets.QBoxLayout.addWidget(MplCanvas)
Supported signatures:
  PySide2.QtWidgets.QBoxLayout.addWidget(PySide2.QtWidgets.QWidget, int = 0, PySide2.QtCore.Qt.Alignment = Default(Qt.Alignment))
  PySide2.QtWidgets.QBoxLayout.addWidget(PySide2.QtWidgets.QWidget)

Basically I can't add the MplCanvas as a widget.

Seems it is not trivial to attach matplotlib to a qwidget...


Solution

  • The answer has 2 parts. First, the way that I defined MplCanvas and MplWidget works but for versions of matplotlib higher than what I had so without going into details, this needed to be slightly altered for my version of matplotlib. But this is not the essence of the issue. I wrote my project gui using qt designer with PyPlot2. Indeed for the gui itself I used the correct script pyplot-uic to convert the ui into python code. When I wrote a stand alone project and tried to plot a pyplot graph on a widget in the main window suddenly the setLayout didn't give errors that the input was not of the correct type. However, when I used a resource (qrc) to add an icon to the main window - the errors returned. I noticed that I used the qt script to convert the resource arc file to a python file. This python file, therefore, imported qt and not pyside2 objects. Since the resource import is pretty much at the beginning, this really caused the confusion between pyqt and pyside2 - they are, as said before, incompatible. When I reran the conversion from resources to python file using the pyside2 script (pyside2-rcc) then the python file imported objects from pyside2. Now there is no confusion with pyside2 and pyqt so the incompatibility was gone and everything (again - changing the classes to be compatible with my version of matplotlib) worked. I succeeded to put qwidget "windows" on a dialog and use icons via qt-designer and have everything work including plotting matplotlib-pyplot "plots" on the qwidget windows. If anyone is interested in seeing exactly how I did this, I can supply code. I'm trying to be brief here so I refrained from posting a bunch of code. The bottom line is - this does work. If it doesn't work, than the issue usually is that pyqt and pyside2 cannot live together so sometimes one needs to seek and search for pyqt imports in the event that the code behaves weirdly. In my case it was the incorrect conversion from qrc to python - inconsistent with the desire to use pyside2 and the qt within throughout the code.