简体   繁体   中英

Embed Matplotlib Graph in a PyQt5

I am trying to embed matplotlib graph into PqQt5, I have a ready function to call them, however, I have no success with it. The code for the graph is as follow:

import re
import os
import sys
import json 
import numpy as np
import pylab
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt



BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = BASE_DIR + os.path.sep + 'data'
if not os.path.exists(DATA_DIR ):
    popup_critical('Грешен път до данните')

with open('./data/71161.json', 'r') as f:
    data = json.load(f)

for distro in data:
    downwelling_radiance = np.array(distro['spectra']['ld']['data'])
    irradiance_reflectance = np.array(distro['spectra']['r0minuscorr']['data'])
    downwelling_irradiance = np.array(distro['spectra']['ed']['data'])
    upwelling_radiance = np.array(distro['spectra']['lu']['data'])
   wavelength = np.array(distro['spectra']['wavelength']['data'])
   reflectance = np.array(distro['spectra']['r0minus']['data'])
   date = np.array(distro['meta']['date'])
   name = np.array(distro['meta']['id'])


def radiance_plot():

    fig = plt.figure(figsize=(14, 8))
    plt.title("Plot")
    plt.xlabel('Wavelength [nm]')
    plt.ylabel('Radiance [W/(m^2xnmxsr)]')

    ax=plt.gca()
    ax.set_xlim(400,800) 

    plt.grid(True, color='skyblue')

    plt.plot(
        wavelength, downwelling_radiance,  'orange',
    wavelength, upwelling_radiance,  'burlywood',
    lw=3,
    )

    print(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) > 
    max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02))
    print(list(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) > 
    max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02)))

    plt.ylim(0.00)
    plt.yticks(list(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) 
    > max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02)))

    plt.legend(['Upwelling radiance', 'Downwelling radiance'], loc='upper left')

    plt.twinx(ax = None)
    plt.plot(
        wavelength, downwelling_irradiance, 'c',
        lw=3, 
    )

    plt.grid(True)
    plt.ylabel('Irradiance [W/(m^2xnm)]')
    plt.yticks(list(np.arange(0, max(downwelling_irradiance + 0.2), 0.2)))
    plt.legend(['Downwelling irradiance'], loc='upper right')
    fig.tight_layout()
    return plt.show()

def reflectance_plot():

    fig = plt.figure(figsize=(14, 8))
    plt.title("Plot")
    plt.xlabel('Wavelength [nm]')
    plt.ylabel('Reflectance [-]')

    ax=plt.gca()
    ax.set_xlim(400,800) 

    plt.grid(True)
    plt.plot(
        wavelength, reflectance,  'burlywood',
        lw=3,
        )

    plt.ylim(0.00)
    plt.yticks(list(np.arange(0, max(reflectance + 0.02), step=0.01)))
    plt.legend(['Radiance'], loc='upper right')
    fig.tight_layout()
    return plt.show()

def date_print():
    return date


def name_print():
    return name

#radiance_plot()
#reflectance_plot()
#name_print()
#date_print()

The code above prints the graphs, now I want to print the graphs in PyQt5 so that when the user presses button 1 it prints the first graph and the second button prints the second graph. There is the code for the PyQt5

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QListWidget, 
QMainWindow, QPushButton
import sys
from PyQt5.QtGui import QIcon, QFont
from read import date_print, name_print, radiance_plot, reflectance_plot
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np

class Window(QWidget):
    def __init__(self):
        super().__init__()


        self.setGeometry(200, 200, 400, 300)
        self.setWindowTitle("Python and Matplotlib")
        self.create_list()
        self.myUI()
        #chart = Canvas(self)

    def create_list(self):
        vbox = QVBoxLayout()

        self.list_widget = QListWidget()


        self.list_widget.insertItem(0, "File1")
        self.list_widget.insertItem(1, "File2")
        self.list_widget.insertItem(2, "file3")
        self.list_widget.setStyleSheet("background-color:red")
        self.list_widget.setFont(QFont("Sanserif", 15))

        self.list_widget.clicked.connect(self.item_clicked)

        self.label = QLabel("")
        self.setFont(QFont("Sanserif", 14))
        self.label.setStyleSheet("color:green")

        vbox.addWidget(self.list_widget)
        vbox.addWidget(self.label)
        self.setLayout(vbox)
    

    def item_clicked(self):
        item = self.list_widget.currentItem()
        self.label.setText("You have selected file:" + str(name_print()) + "; Date: " + 
        str(date_print()))

    def myUI(self):

        #canvas = Canvas(self, width=8, height=4)
        #canvas.move(0,0)

        butt = QPushButton("Click Me", self)
        butt.move(100, 100)

        butt2 = QPushButton("Click Me 2", self)
        butt2.move(250, 100)
"""
class Canvas(FigureCanvas):
    def __init__(self, parent):
        fig, self.ax = plt.subplots(figsize=(5,4), dpi = 50)
        super().__init__(fig)
        self.setParent(parent)
    
        self.plot()

    def plot(self):

        ref = self.figure.add_subplot(111)
        ref.pie(reflectance_plot(), labels=labels)
    
"""

App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec_())

I tried to connect them however I was not able to print anything. I found a way to print a graph however it does not work in my case

import sys
import matplotlib
matplotlib.use('Qt5Agg')

from PyQt5 import QtCore, QtWidgets

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from read import wavelength, downwelling_radiance, upwelling_radiance

import pandas as pd


class MplCanvas(FigureCanvasQTAgg):

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


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=10, height=5, dpi=100)
        
        # Create our pandas DataFrame with some simple
        # data and headers. 

        

        df = pd.DataFrame([
              [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        
        df.plot(ax=sc.axes)

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.show()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()

Any idea how to connect them

I've created this minimal example for you, showing how to embed matplotlib plotting into your pyqt5 app (works for pyside2 just the same).
I saw in your code, that you kept calling to matplotlib.pyplot. You're not supposed to talk to pyplot when embedding in your gui backend. The example also illustrates, how you can set your grid, legend, etc. via calling methods on the axis/figure object and not via plt.
There are 3 buttons, play around with it, and you'll quickly get how to embed matplotlib and how to handle your “custom plot widget”. EDIT: With keyboard keys 1, 2, 3 you can call the plot methods as well.

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
# in case of pyside: just replace PyQt5->PySide2

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
from matplotlib.figure import Figure

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

        fig = Figure(figsize=(5, 5))
        self.can = FigureCanvasQTAgg(fig)
        self.toolbar = NavigationToolbar2QT(self.can, self)
        layout = qtw.QVBoxLayout(self)
        layout.addWidget(self.toolbar)
        layout.addWidget(self.can)

        # here you can set up your figure/axis
        self.ax = self.can.figure.add_subplot(111)

    def plot_basic_line(self, X, Y, label):
        # plot a basic line plot from x and y values.
        self.ax.cla() # clears the axis
        self.ax.plot(X, Y, label=label)
        self.ax.grid(True)
        self.ax.legend()
        self.can.figure.tight_layout()
        self.can.draw()

class MyQtApp(qtw.QWidget):
    def __init__(self):
        super(MyQtApp, self).__init__()

        # layout
        self.mpl_can = MplWidget(self)
        self.btn_plot1 = qtw.QPushButton('plot1', self)
        self.btn_plot2 = qtw.QPushButton('plot2', self)
        self.btn_plot3 = qtw.QPushButton('scatter', self)
        self.layout = qtw.QVBoxLayout(self)
        self.layout.addWidget(self.mpl_can)
        self.layout.addWidget(self.btn_plot1)
        self.layout.addWidget(self.btn_plot2)
        self.layout.addWidget(self.btn_plot3)
        self.setLayout(self.layout)

        # connects
        self.btn_plot1.clicked.connect(self.plot1)
        self.btn_plot2.clicked.connect(self.plot2)
        self.btn_plot3.clicked.connect(self.plot_scatter)

    def keyPressEvent(self, event):
        if event.key() == qtc.Qt.Key_1: self.plot1()
        elif event.key() == qtc.Qt.Key_2: self.plot2()
        elif event.key() == qtc.Qt.Key_3: self.plot_scatter()

    def plot1(self):
        X, Y = (1, 2), (1, 2)
        self.mpl_can.plot_basic_line(X, Y, label='plot1')

    def plot2(self):
        X, Y = (10, 20), (10, 20)
        self.mpl_can.plot_basic_line(X, Y, label='plot2')
    
    def plot_scatter(self):
        X, Y = (1, 7, 9, 3, 2), (3, 6, 8, 2, 11)
        self.mpl_can.ax.scatter(X, Y, label='scatter')
        self.mpl_can.ax.legend()
        self.mpl_can.can.draw()


if __name__ == '__main__':
    app = qtw.QApplication([])
    qt_app = MyQtApp()
    qt_app.show()
    app.exec_()

https://www.mfitzp.com/tutorials/plotting-matplotlib/ is an awesome more in depth tutorial on how to embed matplotlib into pyqt5, but I think for your purpose, above very basic example will suffice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM