web-dev-qa-db-fra.com

Tracé de la figure matplotlib dans QWidget à l'aide du formulaire Qt Designer et de PyQt5

Je ne comprends pas la meilleure façon de lier une figure matplotlib à un formulaire créé à partir de Qt Designer. J'ai un formulaire que j'ai créé dans QtDesigner puis compilé en python via pyuic5. Mon programme principal est:

import app_framework as af
import matplotlib
from PyQt5 import QtWidgets
import sys

matplotlib.use('Qt5Agg')

app = QtWidgets.QApplication(sys.argv)
form = af.MyApp()
form.show()
app.exec_()

où myApp appelle le formulaire app_framework.py créé à partir de Qt Designer puis converti par pyuic5 (design.py):

from PyQt5.QtWidgets import QApplication, QMainWindow
import design

class MyApp(QMainWindow, design.Ui_mainWindow):
   def __init(self):
       super(self.__class__, self).__init__()
       <button initializations>
       <function definitions for button callbacks>

Je ne sais pas où dans ce cadre je peux lier une figure matplotlib à un widget vide prédéfini dans QtDesigner, ou quelque chose de ce genre, afin que je puisse tracer de nouvelles données dans la fenêtre de l'interface graphique au fur et à mesure que les choses se produisent (texte saisi, bouton Pousser , etc.)

J'ai trouvé des discussions ici sur DONC et site de matplotlib , mais je ne suis pas sûr de comprendre le bon processus de création l'espace pour ce widget dans le formulaire Qt Designer, puis en liant un tracé et/ou en créant un widget post hoc, puis en liant et en traçant.

Ce que j'ai fait jusqu'à présent, c'est créer un QWidget vide dans Qt Creator, puis après la compilation de pyuic5, je modifie le fichier design.py comme suit:

from PyQt5 import QtCore, QtGui, QtWidgets

# **** ADDED THIS
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
# **** 

class Ui_mainWindow(object):
    def setupUi(self, mainWindow):
        <mainWindow setup stuff>
        self.centralwidget = QtWidgets.QWidget(mainWindow)

        # ****ALTERED THIS FROM self.plotWidget = QtWidgets.QWidget(self.centralWidget)
        self.plotWidget = MplWidget(self.centralWidget)
        # ***** 

        self.plotWidget.setGeometry(QtCore.QRect(20, 250, 821, 591))
        self.plotWidget.setObjectName("plotWidget")

  # **** ADDED THIS 
class MplCanvas(Canvas):
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        Canvas.__init__(self, self.fig)
        Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        Canvas.updateGeometry(self)


class MplWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.canvas = MplCanvas()
 # ***********

puis app_framework.py comme:

from PyQt5.QtWidgets import QApplication, QMainWindow
import design

class MyApp(QMainWindow, design.Ui_mainWindow):
   def __init(self):
       super(self.__class__, self).__init__()
       self.setupUi(self)
       self.pushButton_plotData.clicked.connect(self.plot_data)

    def plot_data(self):
        x=range(0, 10)
        y=range(0, 20, 2)
        self.plotWidget.canvas.ax.plot(x, y)
        self.plotWidget.canvas.draw()

Je pensais que cela fonctionnerait, mais lorsque je clique sur le bouton poussoir de l'intrigue, rien ne se passe. Il ne se bloque pas, il ne trace rien. Je suppose que je manque quelque chose de fondamental pour tracer une figure et/ou une toile matplotlib dans ce widget vide.

12
launchpadmcquack

J'ai trouvé la solution à l'aide de ce SO post , qui renvoie à ce chapitre échantillon gratuit d'un livre que je vais probablement avoir besoin d'acheter. Comme mentionné dans un certain nombre d'autres articles, il faut "Promouvoir" le QtWidget vide en MplWidget en utilisant l'en-tête mplwidget. Après avoir fait cela, puis exécuté la commande pyuic5, "de mplwidget import MplWidget" apparaîtra dans le fichier design.py qui peut être mis à jour à tout moment sans se soucier de l'écrasement. Ensuite, créez un fichier mplwidget.py avec:

# Imports
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
import matplotlib

# Ensure using PyQt5 backend
matplotlib.use('QT5Agg')

# Matplotlib canvas class to create figure
class MplCanvas(Canvas):
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        Canvas.__init__(self, self.fig)
        Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        Canvas.updateGeometry(self)

# Matplotlib widget
class MplWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)   # Inherit from QWidget
        self.canvas = MplCanvas()                  # Create canvas object
        self.vbl = QtWidgets.QVBoxLayout()         # Set box for plotting
        self.vbl.addWidget(self.canvas)
        self.setLayout(self.vbl)

Ensuite, le cadre d'application peut être comme je l'avais précédemment. Lors de l'exécution et en appuyant sur le bouton de tracé, la figure apparaît comme prévu. Je pense que j'avais pratiquement tout fait pour "Promouvoir" le QWidget à la main en manquant juste les trucs vbl, mais tout cela serait écrasé à chaque fois que le formulaire Qt Designer est édité.

app_framework.py:

from PyQt5.QtWidgets import QApplication, QMainWindow
import design

class MyApp(QMainWindow, design.Ui_mainWindow):
   def __init(self):
       super(self.__class__, self).__init__()
       self.setupUi(self)
       self.pushButton_plotData.clicked.connect(self.plot_data)

    def plot_data(self):
        x=range(0, 10)
        y=range(0, 20, 2)
        self.plotWidget.canvas.ax.plot(x, y)
        self.plotWidget.canvas.draw()

main.py:

from PyQt5 import QtWidgets
import sys

# Local Module Imports
import app_framework as af

# Create GUI application
app = QtWidgets.QApplication(sys.argv)
form = af.MyApp()
form.show()
app.exec_()
12
launchpadmcquack